Cast Trait Object to Implementation

I'm trying to have a HashMap of function pointers, so I have the following setup:

type PrefixFunction = fn(&mut Parser) -> Box<dyn Expression>;

struct Parser {
     prefix_fns: HashMap<TokenType, PrefixFunction>
}

impl Parser {
  pub fn from() -> Parser {
    return Parser {
      prefix_fns: HashMap::from([
        (TokenType::Identifier, parse_identifier as PrefixFunction),
       ])
    }
  }
}

now Identifier (not TokenType::Identifier, I know its confusing) is an Expression and my parse_identifier is defined this way:

pub fn parse_identifier(parser: &mut Parser) -> Box<Identifier> {
  Box::new(Identifier {
    name: String::from(&parser.current_token.literal)
  })
}

Is there a way to cast a fn(&mut Parser) -> Box<Identifier> to a fn(&mut Parser) -> Box<dyn Expression>, or a better way to achieve this? I don't want to make the parse_identifier return a Box<dyn Expression> to avoid casting afterward (after initialisation)

Do you need parse_identifier to return the concrete type? Otherwise, you can easily coerce it right there by having it return Box<dyn Expression -- your Box::new will automatically be unsized. (nevermind, you answered that...)

Otherwise, you'll have to wrap the function to "cast" it, in order to create that conversion from a thin Box<Identifier> to a fat pointer Box<dyn Expression>. A closure will do fine:

|parser| -> Box<dyn Expression> { parse_identifier(parser) } // coerce to unsized Box

A closure can still be used as a simple fn pointer if it doesn't capture anything.

1 Like

Could you give an example for the first solution ? I'm not familiar with the coercing thing

And for the closure thing, should I just add that to my HashMap::From, like:

prefix_fns: HashMap::from([
  (TokenType::Identifier, |parser| -> Box<dyn Expression> { parse_identifier(parser) }),
])

Same idea, different phrasing:

(TokenType::Identifier, (|x| parse_identifier(x)) as PrefixFunction),

The original suggestion gave a type mismatch error, then after as _, a typical higher-ranked closure error, but eventually worked like so:

(TokenType::Identifier, |x: &mut _| -> Box<dyn Expression> { parse_identifier(x) } as _ ),
2 Likes

This is working as expected, Thanks for that.