Problem with lifetimes and traits

I keep getting problems with lifetimes when I use a trait, but not when I directly use a type that implements the trait.

I have recreated the problem in a few lines of code, with the inferred types shown.

struct Token<'src> {
    slice: &'src str,
}

fn lex<'src>(source: &'src str) -> Vec<Token<'src>> {
    vec![]
}

struct Parser<'a, 'src> {
    tokens: &'a [Token<'src>],
}

trait Parse<'a, 'src> {
    fn parse(parser: &mut Parser<'a, 'src>) -> Self;
}

struct Node<'src> {
    slice: &'src str,
}

impl<'a, 'src> Parse<'a, 'src> for Node<'src> {
    fn parse(parser: &mut Parser<'a, 'src>) -> Self {
        Self { slice: parser.tokens[0].slice }
    }
}

fn test<'src, P>(source: &'src str)
where
    P: Parse<'src, 'src>,
{
    let tokens: Vec<Token<'_>> = lex(source); // Inferred type

    // tokens does not live long enough
    // borrowed value does not live long enough
    let mut parser: Parser<'_, '_> = Parser { tokens: &tokens }; // Inferred type
    let _ = P::parse(&mut parser);
}

When I create Parser in test, I get an error saying that tokens does not live long enough, though that error doesn't occur when I don't pass the parser through Parse::parse.

fn test<'src>(source: &'src str) {
    let tokens: Vec<Token<'_>> = lex(source); // Inferred type

    let mut parser: Parser<'_, '_> = Parser { tokens: &tokens }; // Inferred type
    let _ = Node::parse(&mut parser);
}

But when I directly use a type implementing the trait, shown in the codeblock above, I don't get the issue anymore.

I'm guessing it's the compiler not being able to know the lifetimes of the type implementing Parse, though I wasn't able to figure out how to make the compiler see them.

How would I get the tokens from lex, and pass it into P::parse, while having tokens live long enough for it?

You reference the local variable tokens. The parse is needing a specific lifetime (ie not local.)
To get it supporting local;

P: for<'a> Parse<'a, 'src>,

i believe the Parse trait should work like so instead :

struct Token<'src> {
    slice: &'src str,
}

fn lex<'src>(source: &'src str) -> Vec<Token<'src>> {
    vec![]
}

struct Parser<'a, 'src> {
    tokens: &'a [Token<'src>],
}

trait Parse<'src> {
    fn parse(parser: &mut Parser<'_, 'src>) -> Self;
}

struct Node<'src> {
    slice: &'src str,
}

impl<'src> Parse<'src> for Node<'src> {
    fn parse(parser: &mut Parser<'_, 'src>) -> Self {
        Self { slice: parser.tokens[0].slice }
    }
}

fn test<'src, P>(source: &'src str)
where
    P: Parse< 'src>,
{
    let tokens: Vec<Token<'_>> = lex(source); // Inferred type

    // tokens does not live long enough
    // borrowed value does not live long enough
    let mut parser: Parser<'_,'_> = Parser { tokens: &tokens }; // Inferred type
    let _ = P::parse(&mut parser);
}

the 'a doesn't seem very meaingful.

if you must have it in the Parse trait, you would indeed need the bound

, but i don't think this is likely to be the right direction

the problem is with the P: Parse<'src, 'src> trait bound.

short answer: two way to fix it:

  • modify the trait definition

    trait Parse<'src> {
        fn parse<'a>(parser: &mut Parser<'a, 'src>) -> Self;
    }
    
  • change the bound in the signature of test:

    fn test<'src, P>(source: &'src str)
    where
        P: for <'a> Parse<'a, 'src>,
    {
        //...
    }
    

long answer:

a lifetime marker in a function signature, like the 'src in fn test<'src, P>(...), is a generic parameter of the type of the function. since it is a generic parameter, it is instantiated by the caller, in other words, the lifetime 'src must outlive the entire body of the function test().

but the bound P: Parse<'src, src> indicates the function <P as Parse<'src, 'src>>::parse(parser: &mut Parser<'src, src>) must be called with an argument of the type Parser<'src, 'src>, but the parser you created actually has the type Parser<'local, 'src>, where 'local is the lifetime borrowed from the local variable tokens.

so the borrow checker wanted to unify the type Parser<'local, 'src> and Parser<'src, 'src>, but it failed, since 'local CANNOT outlive the variable it was borrowed from, tokens, but 'src must outlive the entire function body.

Intermediate answer:

Lifetimes which parameterize a function,[1] like 'src on fn test, always represent some borrow duration longer than the function body, and you can never borrow local variables for that long -- because all local variables are moved or go out of scope by the end of the function body (which are incompatible with being borrowed).[2]

However, sometimes you need a trait bound that involves a lifetime suitable for borrows of locals, like test does. The way we deal with this is to require the bound holds for all lifetimes, and that's what the for<'a> in the P: for<'a> Parse<'a, 'src> suggestion is doing.

Instead changing the trait to

trait Parse<'src> {
    fn parse(parser: &mut Parser<'_, 'src>) -> Self;
    // Same thing
    // fn parse<'a>(parser: &mut Parser<'a, 'src>) -> Self;
}

effectively means that all implementers must handle all 'a (as defined in your original trait).


  1. or impl, or anything outside the function body -- any lifetime which you can give a name ↩︎

  2. Forcing nested lifetimes to be equal can result in other problems, but that's the immediate cause of the error. ↩︎

I've fixed the problem by moving 'a into Parse::parse, thanks for the people who helped!

When I posted above, my thought was that you might instead of wanted to have the variable not local; (yet best not to suggest so at time.) There is a whole set of other tricks to do so.

That could've worked too, though the more I think about it, 'a isn't actually that important like what Morgane55440 said, so I just elided the lifetime, without needing anything like for.