Why does map takes ownership of Option in Option<T> but not in Option<&T>

There’s two aspects here, better demonstrated if we split up the .as_ref() and the .map(…) step

fn main() {
    let greet: Option<String> = Some("hi".to_string());

    let as_ref = greet.as_ref();
    let mapped = as_ref.map(|e| e);

    dbg!(&mapped, &as_ref, &greet); // all 3 still exist
}

The reason why greet still exists is because the .as_ref method only takes a &self argument of the original Option, and from that produces a new option (of type Option<&String> in this case). Even if that newly produced value was moved, it wouldn’t do anything to the original Option. The reason why even as_ref, too, still exists despite the call to map is because Option<&String> implements Copy.

The original Option is notable not entirely out of the game here. It’s still borrowed. Since the mapped operation kept the reference as a reference, both as_ref and mapped are still considered to borrow from greet, and something like

fn main() {
    let greet: Option<String> = Some("hi".to_string());

    let as_ref = greet.as_ref();
    let mapped = as_ref.map(|e|e);

    drop(greet);
    dbg!(&mapped);
}

thus fails (same thing for trying dbg!(as_ref) instead).

error[E0505]: cannot move out of `greet` because it is borrowed
 --> src/main.rs:7:10
  |
4 |     let as_ref = greet.as_ref();
  |                  -------------- borrow of `greet` occurs here
...
7 |     drop(greet);
  |          ^^^^^ move out of `greet` occurs here
8 |     dbg!(&mapped);
  |          ------- borrow later used here

If on the other hand the mapped operation would create an owned value, e.g. something like .map(|e| e.len()), then mapped would become completely independent of the original greet. E.g. this compiles fine

fn main() {
    let greet: Option<String> = Some("hi".to_string());

    let as_ref = greet.as_ref();
    let mapped = as_ref.map(|e| e.len());

    drop(greet);
    dbg!(&mapped); // but using `&as_ref` here instead would still error
}
2 Likes