shinkou
shinkou

Reputation: 5154

Is there a less verbose way to get a default string from a vector of strings?

I've tried using &str instead of String, but that has compilation errors. I ended up with:

fn main()
{
    let words: Vec<String> = vec!["apple".to_string(), "orange".to_string(), "banana".to_string()];
    let word: String = match words.get(4)
    {
        Some(s) => s.to_string()
        , None => "nil".to_string()
    };
    println!("word: {}", word);
}

Upvotes: 1

Views: 1442

Answers (3)

zetavolt
zetavolt

Reputation: 3207

It is important to remember the coercion rules of strings when deciding between &str and String.

fn main()
{
     let words = vec!["apple", "orange", "banana"]; // Vec<&str>
     let word = match words.get(2) {               // &str
         Some(&s) => s
         None => "nil"
     };
     println!("word: {}", word);
 }

You could consolidate the words and word matching code if length was still a concern for you, but I think this (perhaps including type annotations) is the most succinct without compromising clarity.

Upvotes: 2

Matthieu M.
Matthieu M.

Reputation: 299930

There are two things that will immediately help shorten this program:

  1. Type inference: you very rarely have to write types out
  2. &str: way too many calls to to_string here

I would write it as:

fn main() {
    let words = vec!["apple", "orange", "banana"];
    let word = match words.get(4) {
        Some(&s) => s,
        None => "nil"
    };
    println!("word: {}", word);
}

Note that Vec<T>::get returns an Option<&T>, thus here an Option<&&str>, since we want s to be of type &str (single &), we use Some(&s) as the pattern. Another option would be Some(s) => *s, (here, having s be &&str and dereferencing it).

Of course, this can all be further reduced by looking at the specific API of Option. Somewhat less universal, but Option and Result are very often used in Rust (so much they are in the prelude!!!) and therefore it's worth learning their APIs.

In this case unwrap_or will provide either the value inside Option or the value passed to it:

fn main() {
    let words = vec!["apple", "orange", "banana"];
    let word = *words.get(4).unwrap_or(&"nil");
    println!("word: {}", word);
}

Bit of reference juggling to line-up the types (aka type Tetris) and it's all good.

Upvotes: 7

Lukas Kalbertodt
Lukas Kalbertodt

Reputation: 88736

I'm not exactly sure, what you want to achieve, but this is probably the best way to do exactly what you did in your code:

fn main() {
    let words = vec!["apple", "orange", "banana"];
    let word = words.get(4).cloned().unwrap_or("nil").to_string();
    println!("word: {}", word);
}

The tricky part is cloned(): the get() method will return an optional reference to the value inside of the vector. But since the vector already holds references, we end up with Option<&&str> (two references). The cloned() call removes one reference, thus we have Option<&str>.

Also note that in this example the final to_string() call isn't necessary -- we can do everything only with &str.

Upvotes: 3

Related Questions