Reputation: 731
Consider the following Rust code:
fn foo<'a, T, F, G>(x: &'a mut T, f: F, g: G)
where
T: 'a,
F: Fn(&'a T) -> &'a T,
G: Fn(&'a mut T) -> &'a mut T,
{
{
f(x);
}
g(x);
}
fn main() {
let mut x = 5;
foo(&mut x, |a| a, |a| a);
}
This gives the compiler error:
error[E0502]: cannot borrow `*x` as mutable because it is also borrowed as immutable
--> src/main.rs:10:7
|
8 | f(x);
| - immutable borrow occurs here
9 | }
10 | g(x);
| ^ mutable borrow occurs here
11 | }
| - immutable borrow ends here
I do not understand why the immutable borrow of x
ends on line 11. For one, f(x)
is in an inner scope which ends on line 9. However, the return value of f(x)
is not bound to any variable, so I would think the borrow should end on line 8, and the inner scope shouldn't even be necessary.
Upvotes: 4
Views: 106
Reputation: 30011
Let's consider this example:
fn foo<'a, T, F, G>(x: &'a mut T, mut f: F, g: G)
where
T: 'a,
F: FnMut(&'a T) -> &'a T,
G: Fn(&'a mut T) -> &'a mut T,
{
{
f(x);
}
}
fn main() {
let mut x = 5;
let mut y = std::cell::RefCell::new(&0);
foo(&mut x, |a| { y.replace(&a); a }, |a| a);
}
This is perfectly legal because the function f
is guaranteed to take a reference with the same lifetime as x
, so it can store a reference to x
. But then you can't call g
with x
because f
might have stored x
already.
If you change foo
to:
fn foo<T, F, G>(x: &mut T, mut f: F, g: G)
where
F: FnMut(&T) -> &T,
G: Fn(&T) -> &T,
which is equivalent (due to lifetime elision rules) to:
fn foo<'a, T, F, G>(x: &'a mut T, mut f: F, g: G)
where
T: 'a,
F: for<'b> FnMut(&'b T) -> &'b T,
G: for<'c> Fn(&'c T) -> &'c T,
Then f
isn't allowed to store the reference x
:
error: borrowed data cannot be stored outside of its closure
--> src/main.rs:14:33
|
13 | let mut y = std::cell::RefCell::new(&0);
| ----- -- cannot infer an appropriate lifetime...
| |
| ...so that variable is valid at time of its declaration
14 | foo(&mut x, |a| { y.replace(&a); a }, |a| a);
| --- ^^ cannot be stored outside of its closure
| |
| borrowed data cannot outlive this closure
but calling foo
as foo(&mut x, |a| a, |a| a);
becomes legal.
Upvotes: 4