Reputation: 58805
I don't understand why v
doesn't live long enough in snippet (2)
, but it works in the other cases. If I don't need to clone
the variable in (1)
, why do I need to clone it to get the second case to work?
fn main() {
// (1)
let v = make_vec().unwrap();
let m = v.last();
// (2) v doesn't live long enough!
let m = make_vec()
.and_then(|v| v.last());
// (3) Fixed!
let m = make_vec()
.and_then(|v| v.last().cloned());
}
fn make_vec() -> Option<Vec<u32>> {
Some(vec![1, 2, 3])
}
Upvotes: 1
Views: 271
Reputation: 431599
In the first case, ownership of the Option
passes from make_vec
to the unwrap
call. unwrap
consumes the Option and returns the Vec
, whose ownership passes to the variable v
. The call to last
returns a reference into v
.
In the second case, ownership of the Option
passes from make_vec
to the call to and_then
. and_then
consumes the Option
, and passes ownership of the Vec
to the closure. The call to last
in the closure returns a reference into the Vec
. Since the closure owned the vector but is now finished running, the Vec
will be dropped. The reference into the Vec
would point to memory that is no longer valid, thus the compilation error.
In the third case, ownership of the Option
passes from make_vec
to the call to and_then
. and_then
consumes the Option
, and passes ownership of the Vec
to the closure. The call to last
in the closure returns a reference into the Vec
. The referred-to item is cloned, which creates a new item that is distinct from the Vec
. When the Vec
is dropped after the closure, there are no references into it that could cause a problem.
The types of m
differ between your cases. The first and second cases would return a Option<&u32>
if they both worked. The third case returns a Option<u32>
.
There's a fourth option as well:
let r = make_vec();
let m = r.as_ref().and_then(|v| v.last());
This converts the Option<T>
into an Option<&T>
. This new Option
references the original option and can be consumed by the call to and_then
.
There's a fifth option as well! ^_^ If you are just going to throw away the vector, you can be a bit more explicit about the fact that you want to take it anyway:
let m = make_vec().and_then(|v| v.pop());
Does clone here actually copy things in memory or will the compiler optimise it to effectively pass the ownership of the vector element back? Since this was u32s throughout, I was expecting that they would generally be copied instead of referenced.
Optimizing is a tricky thing, and the only true answer is to look at the optimized output. I would assume that anything that is Copy
and is "small enough" wouldn't really cause a problem. However, I might look at making my code as semantic as possible in order to help the optimizer. I'd probably try to have the pop
variant if that code is what you mean.
Upvotes: 4