gabbi
gabbi

Reputation: 110

Closure which captures outer scope as field of struct in Rust

I am new to Rust and I am trying to create combinator parser in it. Here is the code:

enum Parser<T: Copy + Eq> {
    Ok,
    Eof,
    Satisfy { predicate:  Box<dyn Fn(&T) -> bool> },
    Element { elem : T }
}

impl<T: Copy + Eq> Parser<T> {
    fn parse<'a, P>(&self, input: &'a [T]) -> Option<(&'a [T], P)>
    where
        P: Default,
        T: Into<P>
    {
        match self {
            Parser::Ok => Some((input, P::default())),
            Parser::Eof => match input {
                [] => None,
                _ => Some((input, P::default())),
            },
            Parser::Satisfy { predicate } => match input {
                [] => None,
                [x, rest @ ..] => {
                    if predicate(x) {
                        Some((rest, T::into(*x)))
                    } else {
                        None
                    }
                }
            },
            Parser::Element { elem } => {
                let to_match = *elem;
                Parser::Satisfy { predicate: Box::new(move|x| *x == to_match) }.parse(input)
            }
        }
    }
}

Parser::Ok, Parser::Eof and Parser::Satisfy are working fine but I struggled with realization of Parser::Element (which must much only exact element). I want to realize method parse for it with creation of Parser::Satisfy with predicate of eq between argument x and given elem. But I run into this error:

error[E0310]: the parameter type `T` may not live long enough
  --> src/lib.rs:32:46
   |
32 |                 Parser::Satisfy { predicate: Box::new(move|x| *x == to_match) }.parse(input)
   |                                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
   |
help: consider adding an explicit lifetime bound...
   |
8  | impl<T: Copy + Eq + 'static> Parser<T> {
   |                   +++++++++

I feel there is some problem with lifetimes but I do not know how to fix it.

Upvotes: 0

Views: 61

Answers (1)

Chayim Friedman
Chayim Friedman

Reputation: 70830

dyn Trait contains things. These things may have lifetimes. Therefore, dyn Trait also has a lifetime.

This lifetime, like functions' lifetimes, can be elided. The full rules are somewhat complicated, but when in an ADT, the lifetime is assumed to be 'static. You can specify another lifetime with dyn Trait + 'lifetime.

Thus, the Box<dyn Fn(&T) -> bool> in your enum is actually Box<dyn Fn(&T) -> bool + 'static>.

But the problem is that the function you assign into it captures to_match, of type T. And type T may contain a non-'static lifetime. For example, it may contain &'non_static str. This is the error.

This has two possible solutions.

Either accept that and require T to be 'static:

impl<T: Copy + Eq + 'static> Parser<T> {

Or allow non-'static lifetime for the function:

enum Parser<'a, T: Copy + Eq> {
    Ok,
    Eof,
    Satisfy { predicate:  Box<dyn Fn(&T) -> bool + 'a> },
    Element { elem : T }
}

impl<T: Copy + Eq> Parser<'_, T> {

Upvotes: 2

Related Questions