Cloning function result not enough to solve lifetime issue

I have the following minimal example:

use core::marker::PhantomData;
use std::collections::HashMap;

#[derive(Clone, Copy, Debug)]
pub struct Node<'tree>(PhantomData<&'tree ()>);

#[derive(Clone, Debug)]
pub struct NameDecl<'a> {
    node: Node<'a>,
}

impl<'a> NameDecl<'a> {
    pub fn node(&self) -> &Node<'_> {
        &self.node
    }
}

#[derive(Clone, Debug)]
pub struct VariableDeclaration<'a> {
    names: Vec<NameDecl<'a>>,
}

impl<'a> VariableDeclaration<'a> {
    pub fn names(&'_ self) -> &Vec<NameDecl<'a>> {
        &self.names
    }
}

#[derive(Clone, Debug)]
pub struct SymbolTable<'a> {
    inner: HashMap<String, Node<'a>>,
}

impl<'a> SymbolTable<'a> {
    /// Insert all symbols found in a single variable declaration statement
    pub fn insert(&mut self, foo: &str, decl: VariableDeclaration<'a>) {
        for name in decl.names().iter() {
            self.inner.insert(foo.to_string(), name.node().clone());
        }
    }
}

which gives the following error:

error[E0597]: `decl` does not live long enough
  --> src/lib.rs:37:21
   |
34 | impl<'a> SymbolTable<'a> {
   |      -- lifetime `'a` defined here
35 |     /// Insert all symbols found in a single variable declaration statement
36 |     pub fn insert(&mut self, foo: &str, decl: VariableDeclaration<'a>) {
   |                                         ---- binding `decl` declared here

37 |         for name in decl.names().iter() {
   |                     ^^^^ borrowed value does not live long enough
38 |             self.inner.insert(foo.to_string(), name.node().clone());
   |             ------------------------------------------------------- argument requires that `decl` is borrowed for `'a`
39 |         }
40 |     }
   |     - `decl` dropped here while still borrowed

And I just cannot get my head around why name.node().clone() is not enough here, but if I change it to name.node, everything is fine? Why does rust think that name.node().clone() ends up keeping a reference to decl? What can I do to convince the compiler I want to store a copy here?

(In the real code, decl is actually also stored in SymbolTable, but I don't want to go down the route of storing references to other members of the struct).

My first guess is that returning &Node<'a> would fix it. In fn node, you’re shortening the lifetime of self.node from 'a to '_, which is a problem that directly accessing self.node wouldn’t have.

1 Like

Yes, that was it, thank you!

Why does changing this lifetime affect .clone() though? Is it because it clones the lifetime as well?

Yep, cloning a Node<'a> can’t lengthen the 'a. There is also a thing called “variance” which covers how lifetimes can change / coerce, and the lifetime of Node is covariant (which means it can freely be shortened, but it may or may not be safe to lengthen the lifetime).

So, once the lifetime is shortened, clone() can’t lengthen it.

1 Like