]>
Commit | Line | Data |
---|---|---|
8bb4bdeb | 1 | # Limits of Lifetimes |
c1a9b12d SL |
2 | |
3 | Given the following code: | |
4 | ||
136023e0 | 5 | ```rust,compile_fail |
48663c56 | 6 | #[derive(Debug)] |
c1a9b12d SL |
7 | struct Foo; |
8 | ||
9 | impl Foo { | |
10 | fn mutate_and_share(&mut self) -> &Self { &*self } | |
11 | fn share(&self) {} | |
12 | } | |
13 | ||
14 | fn main() { | |
15 | let mut foo = Foo; | |
16 | let loan = foo.mutate_and_share(); | |
17 | foo.share(); | |
48663c56 | 18 | println!("{:?}", loan); |
c1a9b12d SL |
19 | } |
20 | ``` | |
21 | ||
48663c56 XL |
22 | One might expect it to compile. We call `mutate_and_share`, which mutably |
23 | borrows `foo` temporarily, but then returns only a shared reference. Therefore | |
24 | we would expect `foo.share()` to succeed as `foo` shouldn't be mutably borrowed. | |
c1a9b12d SL |
25 | |
26 | However when we try to compile it: | |
27 | ||
28 | ```text | |
0bf4aa26 | 29 | error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable |
48663c56 | 30 | --> src/main.rs:12:5 |
0bf4aa26 | 31 | | |
48663c56 | 32 | 11 | let loan = foo.mutate_and_share(); |
0bf4aa26 | 33 | | --- mutable borrow occurs here |
48663c56 | 34 | 12 | foo.share(); |
0bf4aa26 | 35 | | ^^^ immutable borrow occurs here |
48663c56 | 36 | 13 | println!("{:?}", loan); |
c1a9b12d SL |
37 | ``` |
38 | ||
39 | What happened? Well, we got the exact same reasoning as we did for | |
40 | [Example 2 in the previous section][ex2]. We desugar the program and we get | |
41 | the following: | |
42 | ||
136023e0 | 43 | <!-- ignore: desugared code --> |
c1a9b12d SL |
44 | ```rust,ignore |
45 | struct Foo; | |
46 | ||
47 | impl Foo { | |
48 | fn mutate_and_share<'a>(&'a mut self) -> &'a Self { &'a *self } | |
49 | fn share<'a>(&'a self) {} | |
50 | } | |
51 | ||
52 | fn main() { | |
48663c56 XL |
53 | 'b: { |
54 | let mut foo: Foo = Foo; | |
55 | 'c: { | |
56 | let loan: &'c Foo = Foo::mutate_and_share::<'c>(&'c mut foo); | |
57 | 'd: { | |
58 | Foo::share::<'d>(&'d foo); | |
59 | } | |
60 | println!("{:?}", loan); | |
61 | } | |
c1a9b12d SL |
62 | } |
63 | } | |
64 | ``` | |
65 | ||
66 | The lifetime system is forced to extend the `&mut foo` to have lifetime `'c`, | |
48663c56 | 67 | due to the lifetime of `loan` and `mutate_and_share`'s signature. Then when we |
c1a9b12d SL |
68 | try to call `share`, and it sees we're trying to alias that `&'c mut foo` and |
69 | blows up in our face! | |
70 | ||
71 | This program is clearly correct according to the reference semantics we actually | |
72 | care about, but the lifetime system is too coarse-grained to handle that. | |
73 | ||
136023e0 | 74 | ## Improperly reduced borrows |
48663c56 | 75 | |
6a06907d | 76 | The following code fails to compile, because Rust doesn't understand that the borrow |
48663c56 XL |
77 | is no longer needed and conservatively falls back to using a whole scope for it. |
78 | This will eventually get fixed. | |
79 | ||
136023e0 | 80 | ```rust,compile_fail |
48663c56 | 81 | # use std::collections::HashMap; |
48663c56 XL |
82 | # use std::hash::Hash; |
83 | fn get_default<'m, K, V>(map: &'m mut HashMap<K, V>, key: K) -> &'m mut V | |
84 | where | |
85 | K: Clone + Eq + Hash, | |
86 | V: Default, | |
87 | { | |
88 | match map.get_mut(&key) { | |
89 | Some(value) => value, | |
90 | None => { | |
91 | map.insert(key.clone(), V::default()); | |
92 | map.get_mut(&key).unwrap() | |
93 | } | |
94 | } | |
95 | } | |
96 | ``` | |
c1a9b12d | 97 | |
6a06907d XL |
98 | Because of the lifetime restrictions imposed, `&mut map`'s lifetime |
99 | overlaps other mutable borrows, resulting in a compile error: | |
100 | ||
101 | ```text | |
102 | error[E0499]: cannot borrow `*map` as mutable more than once at a time | |
103 | --> src/main.rs:12:13 | |
104 | | | |
105 | 4 | fn get_default<'m, K, V>(map: &'m mut HashMap<K, V>, key: K) -> &'m mut V | |
106 | | -- lifetime `'m` defined here | |
107 | ... | |
108 | 9 | match map.get_mut(&key) { | |
109 | | - --- first mutable borrow occurs here | |
110 | | _____| | |
111 | | | | |
112 | 10 | | Some(value) => value, | |
113 | 11 | | None => { | |
114 | 12 | | map.insert(key.clone(), V::default()); | |
115 | | | ^^^ second mutable borrow occurs here | |
116 | 13 | | map.get_mut(&key).unwrap() | |
117 | 14 | | } | |
118 | 15 | | } | |
119 | | |_____- returning this value requires that `*map` is borrowed for `'m` | |
120 | ``` | |
121 | ||
b039eaaf | 122 | [ex2]: lifetimes.html#example-aliasing-a-mutable-reference |