vectorcollectionsrustmove-semantics

How can I take an item from a Vec in Rust?


I'm looking for a method that consumes a Vec and returns one element, without the overhead of restoring Vec's invariants the way remove and swap_remove do:

fn take<T>(vec: Vec<T>, index: usize) -> Option<T>

However, I can't find such a method. Am I missing something? Is this actually unsafe or impossible?

This is a different question from Built in *safe* way to move out of Vec<T>? There the goal was a remove method that didn't panic on out of bounds access and returned a Result. I'm looking for a method that consumes a Vec and returns one of the elements. None of the answers to the above question address my question.


Solution

  • You can write your function like this:

    fn take<T>(mut vec: Vec<T>, index: usize) -> Option<T> {
        if vec.get(index).is_none() {
            None
        } else {
            Some(vec.swap_remove(index))
        }
    }
    

    The code you see here (get and swap_remove) is guaranteed O(1).

    However, kind of hidden, vec is dropped at the end of the function and this drop operation is likely not O(1), but O(n) (where n is vec.len()). If T implements Drop, then drop() is called for every element still inside the vector, meaning dropping the vector is guaranteed O(n). If T does not implement Drop, then the Vec only needs to deallocate the memory. The time complexity of the dealloc operation depends on the allocator and is not specified, so we cannot assume it is O(1).


    To mention another solution using iterators:

    fn take<T>(vec: Vec<T>, index: usize) -> Option<T> {
        vec.into_iter().nth(index)
    }
    

    While Iterator::nth() usually is a linear time operation, the iterator over a vector overrides this method to make it a O(1) operation. Of course, if T implements Drop, this again is an O(n) function as n elements need to be dropped.