Answer array

Bob
Bob in Rust
const ANSWERS: &'static [&'static str] = &[
    "Whatever.",
    "Sure.",
    "Whoa, chill out!",
    "Calm down, I know what I'm doing!",
];

pub fn reply(msg: &str) -> &str {
    let message = msg.trim_end();
    if message.is_empty() {
        return "Fine. Be that way!";
    }

    let is_questioning = if message.ends_with('?') { 1 } else { 0 };
    let is_yelling =
        if message.chars().any(|ch| ch.is_alphabetic()) && message == message.to_uppercase() {
            2
        } else {
            0
        };

    ANSWERS[is_questioning + is_yelling]
}

In this approach you define an array that contains Bob’s answers, and each condition is given a score. The correct answer is selected from the array by using the score as the array index.

The array is defined using the static lifetime annotation for a reference to an array of str references. static means that the value will live for the life of the program. The static annotation can be removed, as described in the Shortening section.

The reference symbols & are needed because the compiler must know the size of each binding. The compiler doesn't know the size of each str, but it does know the size of a reference to a str, which is the size of a pointer for that platform. For a 64-bit system, the size of a pointer is 64 bits. So, each str is defined as a str reference (&str). The array itself is defined as a reference, since the compiler does not know the size of the whole array and its contents.

  • The str method trim_end is used to remove whitespace from the end of the input.

  • If the trimmed input is_empty, then the response for nothing said is returned.

  • The ends_with method is used to determine if the input ends with a question mark. If so, is_questioning is given the value 1, otherwise it is given the value 0.

  • The first half of the yell condition

message.chars().any(|ch| ch.is_alphabetic())

calls the str method chars to create an Iterator for the characters in the trimmed input.

  • The iterated characters are passed into the Iterator method any.
  • any passes each character to a closure using the char method is_alphabetic to ensure there is at least one letter character in the str. This is because the second half of the condition tests that the uppercased input is the same as the input. If the input were only "123" it would equal itself uppercased, but without letters it would not be a yell. The uppercasing is done by using the str method to_uppercase. If the input is a yell, then is_yelling is given the value 2, otherwise it is given the value 0.

The final expression returns the value in the ANSWERS array at the index of the combined values for is_questioning and is_yelling.

Note

Note that the final line is just ANSWERS[is_questioning + is_yelling] This is because the last expression can be returned from a function without using return and a semicolon.

is_yelling is_questioning Index Answer
false false 0 + 0 = 0 "Whatever."
false true 0 + 1 = 1 "Sure."
true false 2 + 0 = 2 "Whoa, chill out!"
true true 2 + 1 = 3 "Calm down, I know what I'm doing!"

Shortening

For defining the array the static lifetime specifiers can be dropped, like so

const ANSWERS: &[&str] = &[
    "Whatever.",
    "Sure.",
    "Whoa, chill out!",
    "Calm down, I know what I'm doing!",
];

A lifetime specifier doesn't change the actual lifetime of a binding. It just makes the lifetime explicit. When the compiler can figure out the lifetime, then the annotation can be dropped, which is called lifetime elision.

11th Sep 2024 · Found it useful?