let greet: Option<String> = Some("hi".to_string());
let mapped = greet.map(|e|e);
dbg!(mapped);
dbg!(greet);
But fails to compile with the error
use of moved value: `greet` [E0382] value used here after move Note: `std::option::Option::<T>::map` takes ownership of the receiver `self`, which moves `greet` Help: you can `clone` the value and consume it, but this might not be your desired behavior
Then I used as_ref to get Option<&String> from Option<String>
let greet: Option<String> = Some("hi".to_string());
let mapped = greet.as_ref().map(|e|e);
dbg!(mapped);
dbg!(greet);
This compiles fine which I was not expecting.
If map takes ownership of the receiver self why does it take ownership of Option when it contains a String but not when it contains &String. I would have assumed the ownership of Option will be moved regardless of what it contains.
And that is the case. You are probably mixing up the types. When you have an Option<&String>, then T = &String (and not, for example, String). So the .map() call takes ownership of the reference to the string, because that's what is inside the Option. And of course, consuming a reference does not in itself invalidate the referred value. (This is not symmetric – consuming a value does invalidate all references to it.)
the map method takes ownership of a reference to an Option<&String> (in the first place) instead of taking ownership of a &String. Either implement Copy though.
Well, but it moves the &Stringtransitively (and that's what we care about), because the Option::<&String>::Some contains a &String. This doesn't contradict what I wrote.
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, bothas_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
}