Example Code:
struct Foo;
impl Foo {
fn foo(&mut self) -> Result<&i32, String> {
match self.bar() {
Some(d) => Ok(d),
None => {
if self.check() {
Err(String::from("Error type 1"))
} else {
Err(String::from("Error type 2"))
}
}
}
}
fn bar(&mut self) -> Option<&i32> {
todo!()
}
fn check(&self) -> bool {
todo!()
}
}
Problem
- In the
Foo::foofunction:self.bar()requires mutable borrow.self.check()requires immutable borrow.
The above code fails to compile. The lifetime checker complains about the two borrow (mutable and immutable).
error[E0502]: cannot borrow `*self` as immutable because it is also borrowed as mutable
--> src/lib.rs:8:20
|
4 | fn foo(&mut self) -> Result<&i32, String> {
| - let's call the lifetime of this reference `'1`
5 | match self.bar() {
| ---------- mutable borrow occurs here
6 | Some(d) => Ok(d),
| ----- returning this value requires that `*self` is borrowed for `'1`
7 | None => {
8 | if self.check() {
| ^^^^^^^^^^^^ immutable borrow occurs here
Questions
Question 1
Why the compiler prevent me to compile this code?
Clearly I am missing something here, but...
The immutable borrow (i.e. self.check()) only happens in the None branch. The previous mutable borrow (i.e. self.bar()) should not bring any impact there, should it?
The same code might be written:
if let Some(d) = self.bar() {
return Ok(d);
}
// What lifetime of `self.bar` is needed at this point?
if self.check() {
// ...
} else {
// ...
}
Question 2
How can I solve this problem?
Please note:
- I do not want to move the check (i.e.
self.check()) before thematch. Merely because of performances:self.checkmight be expensive and in the "cold" path. Ifself.bar()returnsSomeI want to return as soon as possible from the function. - I do not want to to introduce runtime overhead. (E.g., dynamic check for borrow ->
RefCell). - Of course, this is a dummy example. Just for the sake of demonstration. So be aware about it. For example:
&'a i32represent an example returned "data" that requires lifetime. In my real code you can imagine I have a complex object which holds some references (and thus require lifetime). E.g.,struct ComplexObject<'a>.