Pass impl method to another impl method

I'm trying to write a parser.

I want to create a convenience method that will parse a sequence of AST nodes separated by some arbitrary token.

I have this method:

pub fn parse_sequence<T, F> (&mut self, separator: TokenKind, func: F) -> PResult<Vec<T>>
    where
        F: Fn(&mut Parser) -> PResult<T>
    {
        let items = Vec::new();

        items.push(func(self)?);

        while self.tokens.peek().kind == separator {
            self.eat(separator)?;
            items.push(func(self)?);
        }

        Ok(items)
    }

I am calling the function from another method like this:

self.parse_sequence(TokenKind::Comma, Parser::parse_simple_factor)?

With this code I get the following error:

mismatched types
expected type `std::ops::FnOnce<(&mut parse::Parser<'_>,)>`
   found type `for<'r> std::ops::FnOnce<(&'r mut parse::Parser<'_>,)>`rustc(E0308)

This is a tough one. I've tried putting explicit lifetimes all over the parse_sequence function signature, but I always get the same error.

What does this error mean?

Is it possible to pass a method to another method in the same impl?

Have you tried wrapping it in a closure (i.e. |p| p.parse_simple_factor())?

I've found you can't use a fully qualified method (Parser::parse_simple_factor) as a Fn(...). It may be because the method accepts &mut and the compiler can't figure out an appropriate lifetime due to variance... Or something like that.

Thanks for that.

Also just realised that you're the author of that excellent article on geometric constraint solving. It was very informative. When's part two coming? :pleading_face:

Like this?

fn main() {
    let mut s = S;
    s.bar(S::foo);
}

struct S;

impl S {
    fn foo(&mut self) {
        println!("foo!");
    }

    fn bar<F>(&mut self, func: F)
    where
        F: Fn(&mut S),
    {
        func(self);
        println!("bar!");
    }
}

Playground link

Not exactly. It also has to be generic over the return type of the function:

fn bar<F, T>(&mut self, func: F)
where
    F: Fn(&mut S) -> T,
{
    // ...
}

I guess I still don't get it, because this works.

Even if we need the struct to be generic and the return type to match that type, then this works.

I was incorrect earlier. The method should like this:

fn bar<F, T>(&mut self, func: F) -> T
where
    F: Fn(&mut S) -> T,
{
    // ...
   func(self)
}

I just tried that in the playground and it works as well. I'll dig into my code to see what the other differences are.

The error message implies there is a lifetime associated with the struct. Does it have a field which is a reference?

Yeah there is actually. This

It's something about the lifetime of that reference.

Edit: Actually the lifetime introduced by that reference becomes a lifetime on the struct overall, so the function signature needs to reflect that.

This works.

To use your example, just change:

F: Fn(&mut S) -> T,

to

F: Fn(&mut Self) -> T,

That way the compiler knows the lifetime associated with the struct S in the function signature is the same one from the impl block.

Yep. That totally works.

Also, just to show Self isn't magical, you can use

F: Fn(&mut S<'a, T>) -> T,

so long as you no longer elide the lifetime in the impl block, like this:

impl<'a, T: Copy> S<'a, T> {

Playground link

That makes sense.

I couldn't really understand what was happening when Self was used.

I've found using Self is a good habit to have. Since it just means "the thing this impl block is for" it can save a lot of work if you change a type's name or type parameters.