Why doesn’t incr§ work? Should incr(p.clone()) work?
Because p ( the Option<&mut T> ) is attempted to be moved into incr() more than once (thanks to the loop). Option<&mut T> is not copyable or clonable, because if it was, it’d allow a user to create more than one mutable borrow to the same data.
This breaks rusts current borrowing axioms.
Ultimately you are passing ownership the Option, and once passed, it can’t be passed again.
So why does the work around work?
if let &mut Some(ref mut q) = &mut p {
incr(Some(q));
}
So here you borrow the original option as mutable, so that you can then mutably borrow the mutable borrow contained within, you end up with q being &mut &mut p. You then construct a new Option, whose life time is just the method call to incr() and is moved immediately in and thus only ever moved once, and (I assume) thanks to Rust’s type inference and auto deref stuff, it knows it needs to create an Option<&mut i32> and not an Option<&mut &mut i32> and thus auto derefs q from &mut &mut i32 to an &mut i32.
You can see that if you dereference once, manually, it also works showing that it was at least a double reference:
if let &mut Some(ref mut q) = &mut p {
// notice the deref operator next to q
incr(Some(*q));
}
So why does this work:
incr(p.as_mut().map_or(None, |p| Some(p)));
and not this:
incr(p.as_mut());
Well, calling as_mut() on Option<&mut T> gives you a new Option<&mut &mut T>, you might mistakenly think this is a repeat of using the &mut &mut above. However, due to this return type being set, the deref can’t apply.
However, with what you do with the mapping method, it does cause the same case as above:
|p| Some(p)
You are creating a new option which lifetime is the length of incr() function call and moved straight into it, and as with above, the type inference knows that it it needs to create a Option<&mut i32> despite the fact that you would actually be creating a new Option<&mut &mut i32> if type inference never kicked in. So, because it can auto deref p to match the type your are trying to assign the result to, it does, and thus it works.
Or should Option have some other method, supporting incr(p.foo())
With all this being the case though, I’m not sure how you can. Any method that would return an a new Option<&mut T> from an existing Option<&mut T> without consuming the original would give you the same issue as clone and copy.
Disclaimer: I'm not a rust expert, so if I'm wrong, please do let me know.