Limits of Lifetimes

Given the following code:

  1. #[derive(Debug)]
  2. struct Foo;
  3. impl Foo {
  4. fn mutate_and_share(&mut self) -> &Self { &*self }
  5. fn share(&self) {}
  6. }
  7. fn main() {
  8. let mut foo = Foo;
  9. let loan = foo.mutate_and_share();
  10. foo.share();
  11. println!("{:?}", loan);
  12. }

One might expect it to compile. We call mutate_and_share, which mutablyborrows foo temporarily, but then returns only a shared reference. Thereforewe would expect foo.share() to succeed as foo shouldn’t be mutably borrowed.

However when we try to compile it:

  1. error[E0502]: cannot borrow `foo` as immutable because it is also borrowed as mutable
  2. --> src/main.rs:12:5
  3. |
  4. 11 | let loan = foo.mutate_and_share();
  5. | --- mutable borrow occurs here
  6. 12 | foo.share();
  7. | ^^^ immutable borrow occurs here
  8. 13 | println!("{:?}", loan);

What happened? Well, we got the exact same reasoning as we did forExample 2 in the previous section. We desugar the program and we getthe following:

  1. struct Foo;
  2. impl Foo {
  3. fn mutate_and_share<'a>(&'a mut self) -> &'a Self { &'a *self }
  4. fn share<'a>(&'a self) {}
  5. }
  6. fn main() {
  7. 'b: {
  8. let mut foo: Foo = Foo;
  9. 'c: {
  10. let loan: &'c Foo = Foo::mutate_and_share::<'c>(&'c mut foo);
  11. 'd: {
  12. Foo::share::<'d>(&'d foo);
  13. }
  14. println!("{:?}", loan);
  15. }
  16. }
  17. }

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 wetry to call share, and it sees we’re trying to alias that &'c mut foo andblows up in our face!

This program is clearly correct according to the reference semantics we actuallycare about, but the lifetime system is too coarse-grained to handle that.

Improperly reduced borrows

This currently fails to compile, because Rust doesn’t understand that the borrowis no longer needed and conservatively falls back to using a whole scope for it.This will eventually get fixed.

  1. # use std::collections::HashMap;
  2. # use std::cmp::Eq;
  3. # use std::hash::Hash;
  4. fn get_default<'m, K, V>(map: &'m mut HashMap<K, V>, key: K) -> &'m mut V
  5. where
  6. K: Clone + Eq + Hash,
  7. V: Default,
  8. {
  9. match map.get_mut(&key) {
  10. Some(value) => value,
  11. None => {
  12. map.insert(key.clone(), V::default());
  13. map.get_mut(&key).unwrap()
  14. }
  15. }
  16. }