vectorrust

Is there a short way to extract exactly one element from a `Vec`?


I have a reference to a Vec and I want to extract a reference to exactly one element from it, and panic if there are any more elements or zero. The equivalent if I had an array would be:

let [x] = list;

but Vecs are dynamically sized, so that won't work here. I can think of one way to do it with a reference to a Vec, and a couple more that require ownership, but I'm wondering if there's a shorter and simpler way.

Unowned Option 1: Use assert! and indexing

assert_eq!(list.len(), 1);
let x = &list[0];

Owned Option 1: Use try_into()

let [x]: [i32; 1] = list.try_into().unwrap();

Owned Option 2: Use assert! and pop

assert_eq!(list.len(), 1);
let x = list.pop();

So, is there a shorter and clearer way?


Solution

  • You can use a slice pattern combined with let else:

    let v = vec![1u32];
    let &[x] = v.as_slice() else {
        panic!("expected single element"); // or return, break, etc.
    };
    assert_eq!(x, 1);
    

    Playground

    It's not necessarily shorter than your options, but it's clear and reasonably concise, doesn't require ownership or mutation, and can be nicely generalized to more than one element to extract.

    If instead of panicking you want to check the element count and extract x if it's the only element, you can use if let:

    if let &[x] = v.as_slice() {
       // ... use x ...
    }
    

    EDIT

    This answer originally suggested the more verbose match syntax, which was mandatory before let else was introduced in Rust 1.65. It's still useful if you're contributing to a project that supports older compilers, or if you find let else unpalatable:

    let x = match v.as_slice() {
        &[x] => x,
        _ => panic!("expected single element"), // or return, break, etc.
    };