Lifetimes specification and dynamic dispatch problem

The default lifetime for dynamic objects for some reasons is 'static, despite the fact that some variables like Box < dyn SomeTrait > may live much less than the entire lifetime of a program. In combination with custom specified lifetimes this leads to the situations where borrow checker cannot infer an appropriate lifetime, e.g. [E0495].

Here is a toy example, where I cannot figure out, how to specify lifetimes right:

use std::rc::Rc;
use std::cell::RefCell;

trait Tree {
    fn callback(&self, parent: &mut Node);
}

struct A<'t> {
    rule: &'t usize,
    first: Rc<RefCell<Node>>,
}

impl<'t> A<'t> {
    fn next(&self) -> A<'t> {
        A {
            first: Rc::clone(&self.first),
            ..*self
        }
    }
}

impl<'t> Tree for A<'t> {
    fn callback(&self, parent: &mut Node) {
        parent.add_this_way(Rc::new(self.next()))
    }
}

struct B<'t> {
    rule: &'t usize,
    first: Rc<RefCell<Node>>,
}

impl<'t> B<'t> {
    fn next(&self) -> B {
        B {
            first: Rc::clone(&self.first),
            ..*self
        }
    }
}

impl<'t> Tree for B<'t> {
    fn callback(&self, parent: &mut Node) {
        parent.add_another_way(Rc::new(self.next()))
    }
}

struct Node {
    backpointers: Vec<Rc<dyn Tree>>,
}

impl Node {
    fn add_this_way(&mut self, tree: Rc<Tree>) {
        self.backpointers.push(tree)
    }
    fn add_another_way(&mut self, tree: Rc<Tree>) {
        self.backpointers.push(tree);
    }
}


fn main() {
    let x: usize = 0;
    let mut root = Node {
        backpointers: Vec::new(),
    };

    let child = Rc::new(RefCell::new(Node {
        backpointers: Vec::new(),
    }));

    let a = A {
        rule: &x,
        first: Rc::clone(&child),
    };

    let b = B {
        rule: &x,
        first: Rc::clone(&child),
    };

    a.callback(&mut root);
    b.callback(&mut root);
}

playground

Here is the compiler output:

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'t` due to conflicting requirements
  --> src/main.rs:24:42
   |
24 |         parent.add_this_way(Rc::new(self.next()))
   |                                          ^^^^
   |
note: first, the lifetime cannot outlive the lifetime 't as defined on the impl at 22:1...
  --> src/main.rs:22:1
   |
22 | impl<'t> Tree for A<'t> {
   | ^^^^^^^^^^^^^^^^^^^^^^^
   = note: ...so that the types are compatible:
           expected &A<'_>
              found &A<'t>
   = note: but, the lifetime must be valid for the static lifetime...
   = note: ...so that the expression is assignable:
           expected std::rc::Rc<Tree + 'static>
              found std::rc::Rc<Tree>

error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
  --> src/main.rs:44:45
   |
44 |         parent.add_another_way(Rc::new(self.next()))
   |                                             ^^^^
   |
note: first, the lifetime cannot outlive the lifetime 't as defined on the impl at 42:1...
  --> src/main.rs:42:1
   |
42 | impl<'t> Tree for B<'t> {
   | ^^^^^^^^^^^^^^^^^^^^^^^
note: ...so that the type `B<'t>` is not borrowed for too long
  --> src/main.rs:44:40
   |
44 |         parent.add_another_way(Rc::new(self.next()))
   |                                        ^^^^
   = note: but, the lifetime must be valid for the static lifetime...
   = note: ...so that the expression is assignable:
           expected std::rc::Rc<Tree + 'static>
              found std::rc::Rc<Tree>

error: aborting due to 2 previous errors

If I specify a custom lfietime like Vec<Rc<dyn Tree + 's> >, I get other [E0495] errors, like

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter in function call due to conflicting requirements

What should I change in this code to get it pass through compiler?

Please put code between

```rust
...
```

Thanks! I tried with < code > < / code > , it didn't work.

You probably need Rc<dyn Tree + 's>.

Sorry, it was my typo.

As far as I could get with rust Rc<dyn Tree + 's>:

use std::rc::Rc;
use std::cell::RefCell;

trait Tree {
    fn callback<'a>(&'a self, parent: &'a mut Node<'a>);
}

#[derive(Clone)]
struct A<'t, 's> {
    rule: &'t usize,
    first: Rc<RefCell<Node<'s>>>,
}

impl<'t, 's> A<'t, 's> {
    fn next(&self) -> A<'t, 's> {
        A {
            first: Rc::clone(&self.first),
            rule: self.rule,
        }
    }
}

impl<'t, 's> Tree for A<'t, 's> {
    fn callback<'a>(&'a self, parent: &'a mut Node<'a>) {
        parent.add_this_way(Rc::new(self.next()));
    }
}

struct B<'t, 's> {
    rule: &'t usize,
    first: Rc<RefCell<Node<'s>>>,
}

impl<'t, 's> B<'t, 's> {
    fn next(&self) -> B<'t, 's> {
        B {
            first: Rc::clone(&self.first),
            rule: self.rule,
        }
    }
}

impl<'t, 's> Tree for B<'t, 's> {
    fn callback<'a>(&'a self, parent: &'a mut Node<'a>) {
        parent.add_another_way(Rc::new(self.next()))
    }
}

struct Node<'s> {
    backpointers: Vec<Rc<dyn Tree + 's>>,
}

impl<'t> Node<'t> {
    fn add_this_way(&'t mut self, tree: Rc<Tree + 't>) {
        self.backpointers.push(tree)
    }
    fn add_another_way(&'t mut self, tree: Rc<Tree + 't>) {
        self.backpointers.push(tree);
    }
}


fn main() {
    let x: usize = 0;

    let mut root = Node {
        backpointers: Vec::new(),
    };

    let child = Rc::new(RefCell::new(Node {
        backpointers: Vec::new(),
    }));

    {
        let a: Rc<Tree> = Rc::new(A {
            rule: &x,
            first: child.clone(),
        });
        a.callback(&mut root);
    }

    {
        let b = B {
            rule: &x,
            first: Rc::clone(&child),
        };
        b.callback(&mut root);
    }
}

rustc error:

error[E0597]: `a` does not live long enough
  --> src/main.rs:79:9
   |
79 |         a.callback(&mut root);
   |         ^ borrowed value does not live long enough
80 |     }
   |     - `a` dropped here while still borrowed
...
89 | }
   | - borrowed value needs to live until here

error[E0597]: `root` does not live long enough
  --> src/main.rs:79:25
   |
79 |         a.callback(&mut root);
   |                         ^^^^ borrowed value does not live long enough
...
89 | }
   | - `root` dropped here while still borrowed
   |
   = note: values in a scope are dropped in the opposite order they are created

error[E0597]: `b` does not live long enough
  --> src/main.rs:87:9
   |
87 |         b.callback(&mut root);
   |         ^ borrowed value does not live long enough
88 |     }
   |     - `b` dropped here while still borrowed
89 | }
   | - borrowed value needs to live until here

error[E0597]: `root` does not live long enough
  --> src/main.rs:87:25
   |
87 |         b.callback(&mut root);
   |                         ^^^^ borrowed value does not live long enough
88 |     }
89 | }
   | - `root` dropped here while still borrowed
   |
   = note: values in a scope are dropped in the opposite order they are created

warning: variable does not need to be mutable
  --> src/main.rs:66:9
   |
66 |     let mut root = Node {
   |         ----^^^^
   |         |
   |         help: remove this `mut`
   |
   = note: #[warn(unused_mut)] on by default

error: aborting due to 4 previous errors

a dropped here while still borrowed:
a shouldn't be borrowed after line 80 anymore
playground

You specify lifetimes to link bounds to borrows.

For the entire life of a program, hence are unbound as specified by implied + 'static

1 Like

A bound like T: 'static means that the T can be valid for the 'static lifetime but a given instance of it need not live as long itself. In laymen’s terms, it means T doesn’t have any references (except 'static ones).

A Box<dyn SomeTrait + 'static> means the same thing for the underlying value.

These lifetime bounds are a different notion than something like &'a T, which represents a borrow of T for some lifetime 'a.

1 Like

Awesome! This code works like a charm!

Is there any way to trace or have a look at all lifetimes that were inferenced by compiler? My current trial and error approach seems to be not effective enough.