Standard Collections

Collection Ownership

A standard library collection owns every element stored inside it: moving a non-Copy value into a collection transfers ownership to the collection, and dropping the collection automatically runs the destructor (Drop) of every contained element before freeing the backing heap buffer.

Moving Values Into and Out of a Collection

1. Insert (`vec.push(s)`):
   Local binding `s` ──(moves ownership)──> `vec` owns `s`

2. Remove (`let s = vec.pop().unwrap()`):
   `vec` ──(transfers ownership back)────> Local binding `s` owns `s`

3. Drop (`drop(vec)`):
   `vec` drops each remaining element `v[0..len]`, then frees its heap buffer
fn main() {
    let name = String::from("worker-01");
    let mut pool = Vec::new();

    // Transfers ownership of `name` into `pool`:
    pool.push(name);
    // `name` can no longer be used here (`E0382`)

    // `.pop()` transfers ownership of the `String` out of `pool` to `recovered`:
    if let Some(recovered) = pool.pop() {
        println!("Recovered owned string: {recovered}");
    }
}
Recovered owned string: worker-01

Why You Cannot Move Out of an Indexed Collection Element (E0507)

If vec is a Vec<String>, writing let s = vec[0]; does not compile (E0507). Because vec[0] desugars to *vec.index(0) (dereferencing &String), moving the String out of vec[0] would leave an uninitialized "hole" at index 0 inside vec:

// ❌ does not compile
fn main() {
    let vec = vec![String::from("first"), String::from("second")];
    let taken = vec[0];
}
error[E0507]: cannot move out of index of `Vec<String>`
 --> src/main.rs:4:17
  |
4 |     let taken = vec[0];
  |                 ^^^^^^ move occurs because value has type `String`, which does not implement the `Copy` trait
  |
help: consider borrowing here
  |
4 |     let taken = &vec[0];
  |                 +

Four Safe Ways to Extract an Owned Element from a Collection

When you genuinely need an owned T from a collection of non-Copy elements, choose one of these four methods depending on what should happen to the slot in the collection:

Goal Method Effect on the Collection
Remove and return the last element vec.pop() Decrements len by 1 in $O(1)$
Remove at index i (preserving order / $O(1)$ swap) vec.remove(i) / vec.swap_remove(i) Closes the gap by shifting or swapping
Replace slot i with a placeholder (Default or new value) std::mem::take(&mut vec[i]) or std::mem::replace(&mut vec[i], new_val) Leaves the placeholder in vec[i] in $O(1)$
Consume the entire collection into owned elements vec.into_iter() Consumes and frees vec
fn main() {
    let mut messages = vec![String::from("alpha"), String::from("beta")];

    // Extracts ownership of `messages[0]` in O(1), leaving `String::new()` in its place:
    let taken: String = std::mem::take(&mut messages[0]);

    println!("Taken:     '{taken}'");
    println!("Remaining: {messages:?}");
}
Taken:     'alpha'
Remaining: ["", "beta"]