Vector first() with length check before

I am new to rust and started to write some code a couple of days ago.

Question:
Lets say i have this function:

fn get_text() -> Result<String> {
    let texts = vec!["Hello"];

    return match texts.len() {
        0 => Err("text not found"),
        1 => texts.first().cloned().ok_or("text can not be none"),
        _ => Err("Too many texts found")
    }

in the match where I check for 1, I need to .ok_or() the Option and handle the None case. Is there a way to simply ignore None? Or a better way in general?

Because the check for texts.len() == 1 already asserts that the vector has at least one element. Its kinda redundant to also have to check for None, because its impossible that first() returns None.

You can simply use unwrap() since it is impossible for the Option to be null.

1 Like

ahh makes sense. but a way to simply ignore None is not possible? you have to handle the none case? in this case with unwrap() to just silence it

You can match on slices

fn get_text() -> Result<String, &'static str> {
    let texts = vec!["Hello"];

    return match texts[..] {
        [] => Err("text not found"),
        [text] => Ok(text.to_string()),
        _ => Err("Too many texts found")
    }
}
12 Likes

I am glad I asked the question in this forum. thats a nice way, that I didn't thought about.

If you have control over the data structures, using a queue and pop_front could be more ergonomic.

Edit: If the vec only has 1 element a pop will work just as well

Since you're only trying to handle exactly one element, you can also try_into an array:

pub fn get_text(texts: Vec<String>) -> Result<String, String> {
    if texts.is_empty() { return Err("text not found".into()); }
    
    let [text]: [_; 1] = texts.try_into().map_err(|_| "Too many texts found")?;
    Ok(text)
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=943e8168e3f1739707c01631d906e512

I moved texts to a parameter (and Strings instead of &strs) to demonstrate that this doesn't need to copy anything -- it moves the one element of the vector into the array pattern.


Actually, the error type there gives another way to write it that avoids the pre-check:

pub fn get_text(texts: Vec<String>) -> Result<String, String> {
    let [text]: [_; 1] = texts.try_into().map_err(|v: Vec<String>| {
        if v.is_empty() {
            "text not found"
        } else {
            "Too many texts found"
        }
    })?;
    Ok(text)
}

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=b6f6006004fdbeb6b01de5725ad59b88

3 Likes

thanks everyone for the quick help