J-F-Liu / pom

PEG parser combinators using operator overloading without macros.
MIT License
500 stars 31 forks source link

Using `Fn` causes some lifetime issues. #32

Open blakehawkins opened 5 years ago

blakehawkins commented 5 years ago

I'm trying to generalise the approach found in the whitespace example.

I think some changes may be needed in pom.

The following function is illegal:

fn with_indentation<'a, T>(p: Parser<'a, u8, T>) -> Parser<'a, u8, T>
where
    T: 'a + Clone + Sized,
{
    (
        indented() |
        empty().map(|()| vec![])
    ).repeat(0..).map(
        |lines| lines.into_iter().filter(
            |line| line.len() > 0
        ).fold(
            vec![],
            |accum, line| accum.into_iter().chain(
                line.into_iter().chain(vec![b'\n'].into_iter())
            ).collect()
        )
    ).map(move |lines| {
        p.parse(
            &lines
        ).expect("subparse")
    })
}

The error is like this:

170 |   fn with_indentation<'a, T>(p: Parser<'a, u8, T>) -> Parser<'a, u8, T>
    |                              - lifetime `'1` appears in the type of `p`
...
189 | /         p.parse(
190 | |             &lines
    | |             ^^^^^^ borrowed value does not live long enough
191 | |         ).expect("subparse")
    | |_________- argument requires that `lines` is borrowed for `'1`
192 |       })
    |       - `lines` dropped here while still borrowed

There is a problem: map takes Fn, not FnOnce, so it's not possible to guarantee idempotence

J-F-Liu commented 4 years ago

Have you found a workaroud yet? Try to use FnOnce, but cannot compile:

  --> src/parser.rs:45:36
   |
37 |     pub fn map<U, F>(self, f: F) -> Parser<'a, I, U>
   |                            - captured outer variable
...
45 |             (self.method)(input, start).map(|(out, pos)| (f(out), pos))
   |                                             ^^^^^^^^^^^^  -
   |                                             |             |
   |                                             |             move occurs because `f` has type `F`, which does not implement the `Copy` trait
   |                                             |             move occurs due to use in closure
   |                                             move out of `f` occurs here
blakehawkins commented 4 years ago

@J-F-Liu unfortunately I don't have any elegant solution. I've been copy-pasting this kind of code for each call site where this kind of pattern is needed:

    (
        indented() |
        empty().map(|()| vec![])
    ).repeat(0..).map(
        |lines| lines.into_iter().filter(
            |line| line.len() > 0
        ).fold(
            vec![],
            |accum, line| accum.into_iter().chain(
                line.into_iter().chain(vec![b'\n'].into_iter())
            ).collect()
        )
    ).map(move |lines| {
        *owned_parser*.parse(
            &lines
        ).expect("subparse")
    })