Reputation: 8970
I have been trying to implement my own Option::get_or_insert_with
, with a twist: the function that produces the value to insert in the Option
is fallible. This was my first, most reasonable-looking playground attempt:
fn fallible_get_or_insert_with(option: &mut Option<String>) -> Result<&mut String, &'static str> {
if let Some(inner) = option.as_mut() {
return Ok(inner);
};
let inner = if rand::random() {
"Brand new inner value!".to_string()
} else {
return Err("Got unlucky");
};
Ok(option.insert(inner))
}
Disappointingly, I'm getting
error[E0499]: cannot borrow `*option` as mutable more than once at a time
--> src/main.rs:12:8
|
1 | fn fallible_get_or_insert_with(option: &mut Option<String>) -> Result<&mut String, &'static str> {
| - let's call the lifetime of this reference `'1`
2 | if let Some(inner) = option.as_mut() {
| ------ first mutable borrow occurs here
3 | return Ok(inner);
| --------- returning this value requires that `*option` is borrowed for `'1`
...
12 | Ok(option.insert(inner))
| ^^^^^^ second mutable borrow occurs here
For more information about this error, try `rustc --explain E0499`.
Despite my best efforts, I can't seem to understand why I'm double-borrowing. In my head, if the fallible_get_or_insert
does not return at line 3, no reference to option
is held anymore. In my head, I got option.as_mut()
, looked at it, determined that it is not Some
, then dropped it. Why am I not free to mutably borrow option
again, with the insert at line 12?
I tried variations over variations of this function but I can't seem to convince the borrow checker, so I am thinking there is something inherent to this problem that I am missing altogether. Of course, I know I can settle for something like
if option.is_none() {
// Fallibly fill `option`
}
option.as_mut().unwrap()
but I don't understand why I should be forced to do that. The unwrap()
can't fail, so why do I need to go through its unsightly use instead of using more canonical Option
-manipulation primitives?
In an attempt to understand this better, I went and looked at the code of Option::get_or_insert
. To my dismay, even they use .as_mut().unwrap_unchecked()
. So I am thinking there is no way around this. But if I could understand better why I would at least find some peace!
Upvotes: 1
Views: 31