Trivial cast of function pointer


#1

I have a lexical analyzer modeled after what Rob Pike described doing in Go. It uses function pointers to represent the states, and the finite state machine simply invokes functions, gets the next function as a return value, and loops until it gets nil. In Go this is easy, but Rust is a bit trickier.

struct StateFn(fn(&mut Lexer) -> Option<StateFn>);

pub fn lex(name: &str, input: &str) -> Receiver<Token> {
    // code for setting up the channel...

    thread::spawn(move || {
        let mut lexer = Lexer::new(thread_name, sanitized, thread_tx);
        let mut state = lex_start as fn(&mut Lexer) -> Option<StateFn>;
        loop {
            match state(&mut lexer) {
                Some(next) => {
                    let StateFn(state_fn) = next;
                    state = state_fn;
                },
                None => break
            }
        }
    });
    // return the receiver end of the channel...
}

fn lex_start(l: &mut Lexer) -> Option<StateFn> {
    lex_some_other_func
}

// more state functions...

This has compiled without warning and been working for some time, but with the latest build (27901849e 2015-03-25) it is now telling me that the cast on the “let mut state = …” line is trivial. However, I can’t figure out how to take it away and still have it compile. Any advice?

Thanks

Full source: https://github.com/nlfiedler/shiranui/blob/master/src/lexer.rs (see line 389)


#2

Have you tried

let mut state : fn(&mut Lexer) -> Option<StateFn> = lex_start;

Or even just

let mut state = lex_start;

? I don’t know if that would work, but what is the compile error?


#3

Great suggestion. Since the cast was “trivial” it didn’t occur to me to be explicit about the type (after all, wouldn’t that be just another way of saying the same thing?).

BTW, the error for your second suggestion is as follows:

src/lexer.rs:394:29: 394:37 error: mismatched types:
 expected `fn(&mut lexer::Lexer) -> core::option::Option<lexer::StateFn>    {lexer::lex_start}`,
    found `fn(&mut lexer::Lexer) -> core::option::Option<lexer::StateFn>`
(expected fn item,
    found fn pointer) [E0308]
src/lexer.rs:394                     state = state_fn;
                                             ^~~~~~~~

And that led to all sorts of bizarre variations, all of which failed. But since declaring the type works just fine, I’ll use that.

Thanks for your help.


#4

No problem, glad it worked!


#5

FYI: Those errors without the type are because every function item has its own, unique type which includes its name. You have to trigger an implicit cast, which causes the name to be thrown away. I believe this exists so that you can pass a “function pointer” into a generic function, but retain enough information for the compiler to statically dispatch the call.


#6

That makes sense, thanks for the explanation.