Idiomatic expect single element vector

Hi,

I'm writing some code that must handle vectors that must be of a single element.

Following other topics here, I've done:

trait ExpectOne<T> {
    fn expect_one(&self) -> Option<&T>;
}

impl<T> ExpectOne<T> for Vec<T> {
    fn expect_one(&self) -> Option<&T> {
        match &self[..] {
            [] => None,
            [s] => Some(s),
            _ => None,
        }
    }
}

, which works well (I can do v.expect_one().unwrap_or_else(|| panic!(...), but this seems like a very common pattern, so I'm wondering if there's a common way to do this?

A long time ago I wrote the single crate, missing the similar functionality from Kotlin, although that's limited to iterators, not vectors/slices directly. Nowadays itertools' at_most_one would be what to use.

Rather than expect_one as a helper, though, I'd suggest just writing the pattern inline when you need it, e.g.

let [e] = &v else {
    panic!();
};

or better yet, avoid taking/building a slice in the first place.

If this is unhelpful just ignore it, but I'm wondering why you're using a Vec instead of a one element array?

No, that makes sense and is useful context. I'm processing an AST (actually, a Pandoc AST), and I want to extract data from some stuff that could be multiple items, but in my case it must be one.

For example, a table title is a vector of inlines, but I'm looking for table titles which are a single word, so a vector with a single inline.

@CAD97 Thanks! I think actually exactly_one is exactly what I need? I'm a bit puzzled about your other suggestion. Now, I'm a Rust noob and some basic stuff eludes me... I think given my use case exactly_one is best, but I'm unsure of the advantages of the pattern match?

Now I'm writing:

fn parse_table_bodies(bodies: Vec<pandoc::TableBody>) -> Vec<Vec<String>> {
    bodies
        .expect_one()
        .unwrap_or_else(|| panic!("Unexpected table bodies {:?} of len != 1", bodies))
        .body
        .iter()
        .map(rows_to_vec_str)
        .collect()
}

, where I guess I can replace .expect_one() with .iter().exactly_one() and be on my merry way, whereas the match you suggest would be a bit wordier?

I'm having issues because I'm lately writing a lot of code that matches on non-trivial structures (esp. with vectors) and I'm sure there are more compact and clearer ways to do it...

If you can continue the chain, a postfix method will be a bit more "clean" and "point free" (no extra named bindings), but sometimes names, despite being a bit more extra verbosity, can improve code structure. The exact same written with a let-else:

fn parse_table_bodies(tables: &[pandoc::TableBody]) -> Vec<Vec<String>> {
    let [table] = tables else {
        panic!("should have one table body, got {:?}", tables)
    };
    table.body
        .iter()
        .map(rows_to_vec_str)
        .collect()
}

At that point it's mostly just a matter of style. Except to note that if you use into_iter instead, you might be able to eliminate some cloning.

Ah, yes, that's more or less what I thought. Yeah, I need to play with this more, thanks!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.