Nate
Nate

Reputation: 185

Type Error calling closures stored in struct

I've recently started trying to write some toy programs in rust, and I guess I wanted to take a stab at a parser combinator. My naive attempt was this:

enum ParseResult<T> {
    Ok(T),
    Error (String),
}

struct Parser<'a, I,O>{
    Parse: |I|:'a ->ParseResult<O>,
}
impl<'a, I,O,J> Parser<'a, I, O>{
    fn Compose<I,O,K>(self, RHS : Parser<O,K> )->Parser<I,K>{
        Parser
        {
            Parse :  |x:I|
               {
                   let intermediate = self.Parse;
                   match intermediate(x){ //mismatched types: expected `I` but found `I` (expected type parameter but found type parameter)

                       Ok(result) =>
                       {
                           let final = RHS.Parse;
                           final(result) mismatched types: expected `O` but found `0` (expected type parameter but found type parameter)

                       }          
                      Error(result) => Error(result)
                   }
               }
        }
    }
}

I'm pretty sure the Errors indicate something more fundamentally wrong with my approach but I'm not quite sure what.

Upvotes: 2

Views: 117

Answers (1)

Vladimir Matveev
Vladimir Matveev

Reputation: 128111

Indeed, there is a couple of errors in your program.

Currently closures are stack-"boxed", so you can't return them from functions, and that's exactly what happening in your example - you're storing a closure in Parser struct.

Another error is incorrect usage of generic parameters. Even without closures your method wouldn't work: I and O parameters on Compose method are different from those on impl clause, even if they share the same name.

BTW, you don't need to move a closure into intermediate variable. To call closures stored in fields you need to do this:

let intermediate = (self.Parse)(x);

That said, several days ago unboxed closures finally landed. With them it is possible to do what you want, though it is somewhat cumbersome at the moment.

#![feature(unboxed_closures)]

struct Parser<I, O> {
    parse: Box<FnMut<(I,), Result<O, String>>>
}

impl<I, O> Parser<I, O> {
    fn compose<K>(mut self, mut rhs: Parser<O, K>) -> Parser<I, K> {
        Parser {
            parse: box |&mut: x: I| {
                match self.parse.call_mut((x,)) {
                    Ok(r) => rhs.parse.call_mut((r,)),
                    Err(e) => Err(e)
                }
            }
        }
    }
}

This should work but right now it doesn't, and it looks like a bug to me:

<anon>:11:23: 11:33 error: cannot borrow data mutably in an aliasable location
<anon>:11                 match self.parse.call_mut((x,)) {
                                ^~~~~~~~~~
<anon>:12:35: 12:44 error: cannot borrow data mutably in an aliasable location
<anon>:12                     Ok(result) => rhs.parse.call_mut((result,)),
                                            ^~~~~~~~~

When I rewritten this example with manually expanded unboxed closure it worked, so there's definitely some compiler problem here. Unboxed closures are very new, so such things are not unexpected. I've submitted an issue on this.

Upvotes: 2

Related Questions