Comparing string options

Hi guys,

I'm trying to do seemingly the simplest thing in the world, extracting the lexicographically first string in a list, what I've got at this time works but is so unwieldy, there has to be a better way!

fn main() {

    let strings: Vec<String> = vec!("first".to_string(), "second".to_string());
    let mut min: Option<String> = None;
    
    for st in strings {
        // More computation here, cannot use .map() or the like
        if &st < min.as_ref().unwrap_or(&String::from("~")) { // so ugly, all I wanted was "st < min" :-(
            min = Some(st.to_string());
        }
    }
    println!("{:?}", min);
}

(Playground)

Output:

Some("first")

Thanks for any suggestion!
Chris

Like this one?

println!("{:?}", strings.iter().min());

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=088b2593625296347f24d27217ef13c8

5 Likes

If you want to preserve the behavior of ignoring strings that are greater than "~", here's a minor simplification:

    let mut min: Option<&str> = None;
    
    for st in &strings {
        if st.as_str() < min.unwrap_or("~") {
            min = Some(st);
        }
    }
    println!("{:?}", min);

Playground

Though you could also do:

let min = strings.iter().filter(|s| s.as_str() < "~").min();
2 Likes

One more approach, if you want a simple way to write loops like this without unwrap_or or map:

fn min(strings: &[String]) -> Option<&String> {
    let mut iter = strings.iter();
    let mut min = iter.next()?;
    for st in iter {
        if st < min {
            min = st;
        }
    }
    Some(min)
}

fn main() {
    let strings: Vec<String> = vec!("first".to_string(), "second".to_string());
    println!("{:?}", min(&strings));
}
2 Likes