I had a use-case where I wanted to store a function in an enum type
#[derive(Debug)]
enum RispErr {
Reason(String),
}
#[derive(Clone)]
enum RispExp {
Bool(bool),
Symbol(String),
Number(f64),
List(Vec<RispExp>),
Func(fn(&Vec<RispExp>) -> Result<RispExp, RispErr>),
}
In one case, I wanted to create a higher order function to generate these
// simplifying this for the question
// shortening this for brevity
fn make_tonicity_checker(
tone_fn: fn(f64, f64) -> bool,
) -> impl Fn(&Vec<RispExp>) -> Result<RispExp, RispErr> {
return move |args: &Vec<RispExp>| -> Result<RispExp, RispErr> {
tone_fn(1.0, 2.0); // need to use this
return Ok(RispExp::Bool(true));
};
}
I come across errors though when I tried to use the higher order function
fn f() -> () {
RispExp::Func(make_tonicity_checker(|a, b| a > b));
}
mismatched types
expected fn pointer, found opaque type
note: expected type `for<'r> fn(&'r std::vec::Vec<RispExp>) -> std::result::Result<RispExp, RispErr>`
found type `impl for<'r> std::ops::Fn<(&'r std::vec::Vec<RispExp>,)>`rustc(E0308)
main.rs(93, 5): expected fn pointer, found opaque type
I dug deeper and realized that function pointers cannot capture the environment, and hence the error. I tried
Func(Fn(&Vec<RispExp>) -> Result<RispExp, RispErr>),
but, realized this fails too, as it doesn't have a size known at compile time. with some googling, I found that I could potentially pass this in as a type parameter
#[derive(Clone)]
enum RispExp<T>
where
T: Fn(&Vec<RispExp<T>>) -> Result<RispExp<T>, RispErr>,
{
Bool(bool),
Symbol(String),
Number(f64),
List(Vec<RispExp<T>>),
Func(T),
}
but then, in all places where I accept RispExp
, I would need to provide this type parameter. That seems a bit annoying to do, because I would have to repeat where T: Fn(&Vec<RispExp<T>>) -> Result<RispExp<T>, RispErr>,
everywhere.
I could do Func(Box<Fn(&Vec<RispExp>) -> Result<RispExp, RispErr>>)
, but then dyn fn
doesn't implement Clone
.
What would you recommend I do?