Sorry for the bad title, I can’t describe the problem more precisely.
I have a struct S
, a struct Wrapper
that stores a mutable reference to an S
and a wrap
method in S
that may or may not create a Wrapper
:
#[derive(Debug)]
struct S {
id: i32,
}
impl S {
fn wrap(&mut self, secret: &str) -> Result<Wrapper<'_>, ()> {
if secret == "correct" {
Ok(Wrapper {
r: self,
})
} else {
Err(())
}
}
}
#[derive(Debug)]
struct Wrapper<'a> {
r: &'a mut S
}
I want to call this wrap
method multiple times with different secrets until it works. If I do this manually, the borrow checker realizes that the borrow ends once I call unwrap_err
and permits this code:
fn main() {
let mut s = S { id: 1 };
let a = s.wrap("wrong").unwrap_err();
let b = s.wrap("correct").unwrap();
println!("{:?}/{:?}", a, b);
}
But if I wrap this into a function foo
that works for any function that creates a Result<Wrapper<'a>, ()>
, the borrow checker complains about the second call to op
and does not recognize that the borrow ended when I called unwrap_err
for the first result:
fn foo<'a, F>(s: &'a mut S, op: F) -> ((), Wrapper<'a>)
where
F: Fn(&'a mut S, &str) -> Result<Wrapper<'a>, ()>
{
let err = op(s, "wrong").unwrap_err();
let ok = op(s, "correct").unwrap();
(err, ok)
}
fn main() {
let mut s = S { id: 1 };
let (a, b) = foo(&mut s, |s, secret| s.wrap(secret));
println!("{:?}/{:?}", a, b);
}
Why exactly is this failing? What is the difference to the first example? And what can I do to make this work?
Errors:
Compiling playground v0.0.1 (/playground)
error[E0499]: cannot borrow `*s` as mutable more than once at a time
--> src/main.rs:27:17
|
22 | fn foo<'a, F>(s: &'a mut S, op: F) -> ((), Wrapper<'a>)
| -- lifetime `'a` defined here
...
26 | let err = op(s, "wrong").unwrap_err();
| --------------
| | |
| | first mutable borrow occurs here
| argument requires that `*s` is borrowed for `'a`
27 | let ok = op(s, "correct").unwrap();
| ^ second mutable borrow occurs here
error: aborting due to previous error
For more information about this error, try `rustc --explain E0499`.
error: Could not compile `playground`.
To learn more, run the command again with --verbose.