Why do closures with no captured variables not resolve to `fn`?

I was recently writing a piece of code where I had some functions I wanted to store in an array. But I needed to map the output types to the corresponding field of the union. I easily wrote a closure to do this, but it didn’t compile because the closures didn’t match types. I converted it to using functions with the exact same parameters, return type, and body and now it compiles. I wonder why closures with no arguments can’t be trivially converted to fn (&mut Parser) -> Result<ElseExpression, Error> here?

With clusure (doesn’t compile):

fn expect_else_expression(parser: &mut Parser) -> Result<ElseExpression, Error> {
    one_of(
        parser,
        &mut [
            |parser| expect_if_expression_(parser).map(ElseExpression::If),
            |parser| expect_block(parser).map(ElseExpression::Block)
        ][..],
        Error::Expected("else expression", parser.span()),
    )
}

With fn (compiles):

fn expect_else_expression(parser: &mut Parser) -> Result<ElseExpression, Error> {
    fn else_expression_if(parser: &mut Parser) -> Result<ElseExpression, Error> {
        expect_if_expression_(parser).map(ElseExpression::If)
    }
    fn else_expression_block(parser: &mut Parser) -> Result<ElseExpression, Error> {
        expect_block(parser).map(ElseExpression::Block)
    }

    one_of(
        parser,
        &mut [else_expression_if, else_expression_block][..],
        Error::Expected("else expression", parser.span()),
    )
}

It can be converted but you have to tell the compiler to do the conversion, for example:

one_of(
    parser,
    &mut [
        (|parser| expect_if_expression_(parser).map(ElseExpression::If)) as fn(&mut Parser) -> Result<ElseExpression, Error>,
        |parser| expect_block(parser).map(ElseExpression::Block),
    ][..],
    Error::Expected("else expression", parser.span()),
)
4 Likes

The compiler isn’t that good at inferring intent, so when it see two closures in an array it can’t tell what you want so it errors out. Therefore you must tell it that you want fn pointers.

Both closures and regular functions all have individual types, zero-sized apart from closure captures. When you coerce to a general fn type, the size must grow to include a function pointer.

3 Likes