You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
nomicon/src/lifetime-mismatch.md

122 lines
3.3 KiB

# Limits of Lifetimes
Given the following code:
```rust,edition2018,compile_fail
#[derive(Debug)]
struct Foo;
impl Foo {
fn mutate_and_share(&mut self) -> &Self { &*self }
fn share(&self) {}
}
fn main() {
let mut foo = Foo;
let loan = foo.mutate_and_share();
foo.share();
println!("{:?}", loan);
}
```
One might expect it to compile. We call `mutate_and_share`, which mutably
borrows `foo` temporarily, but then returns only a shared reference. Therefore
we would expect `foo.share()` to succeed as `foo` shouldn't be mutably borrowed.
However when we try to compile it:
```text
error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable
--> src/main.rs:12:5
|
11 | let loan = foo.mutate_and_share();
| --- mutable borrow occurs here
12 | foo.share();
| ^^^ immutable borrow occurs here
13 | println!("{:?}", loan);
```
What happened? Well, we got the exact same reasoning as we did for
[Example 2 in the previous section][ex2]. We desugar the program and we get
the following:
```rust,ignore
struct Foo;
impl Foo {
fn mutate_and_share<'a>(&'a mut self) -> &'a Self { &'a *self }
fn share<'a>(&'a self) {}
}
fn main() {
'b: {
let mut foo: Foo = Foo;
'c: {
let loan: &'c Foo = Foo::mutate_and_share::<'c>(&'c mut foo);
'd: {
Foo::share::<'d>(&'d foo);
}
println!("{:?}", loan);
}
}
}
```
The lifetime system is forced to extend the `&mut foo` to have lifetime `'c`,
due to the lifetime of `loan` and `mutate_and_share`'s signature. Then when we
try to call `share`, and it sees we're trying to alias that `&'c mut foo` and
blows up in our face!
10 years ago
This program is clearly correct according to the reference semantics we actually
care about, but the lifetime system is too coarse-grained to handle that.
## Improperly reduced borrows
The following code fails to compile, because Rust doesn't understand that the borrow
is no longer needed and conservatively falls back to using a whole scope for it.
This will eventually get fixed.
```rust,edition2018,compile_fail
# use std::collections::HashMap;
# use std::hash::Hash;
fn get_default<'m, K, V>(map: &'m mut HashMap<K, V>, key: K) -> &'m mut V
where
K: Clone + Eq + Hash,
V: Default,
{
match map.get_mut(&key) {
Some(value) => value,
None => {
map.insert(key.clone(), V::default());
map.get_mut(&key).unwrap()
}
}
}
```
Because of the lifetime restrictions imposed, `&mut map`'s lifetime
overlaps other mutable borrows, resulting in a compile error:
```text
error[E0499]: cannot borrow `*map` as mutable more than once at a time
--> src/main.rs:12:13
|
4 | fn get_default<'m, K, V>(map: &'m mut HashMap<K, V>, key: K) -> &'m mut V
| -- lifetime `'m` defined here
...
9 | match map.get_mut(&key) {
| - --- first mutable borrow occurs here
| _____|
| |
10 | | Some(value) => value,
11 | | None => {
12 | | map.insert(key.clone(), V::default());
| | ^^^ second mutable borrow occurs here
13 | | map.get_mut(&key).unwrap()
14 | | }
15 | | }
| |_____- returning this value requires that `*map` is borrowed for `'m`
```
[ex2]: lifetimes.html#example-aliasing-a-mutable-reference