以枚举类型存储`impl Fn`

时间:2019-04-19 07:57:55

标签: rust

我有一个用例,我想在其中将函数存储为枚举类型

#[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>),
}

在一种情况下,我想创建一个更高阶的函数来生成这些函数

// 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));
    };
}

尽管在尝试使用高阶函数时遇到错误

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

我更深入地研究并意识到,函数指针无法捕获环境,因此无法捕获错误。我尝试过

Func(Fn(&Vec<RispExp>) -> Result<RispExp, RispErr>),

但是意识到这也失败了,因为它在编译时没有已知的大小。进行了一些谷歌搜索,我发现我有可能将其作为类型参数传入

#[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),
}

但是,在所有我接受RispExp的地方,我都需要提供此类型参数。这似乎有点烦人,因为我将不得不在所有地方重复where T: Fn(&Vec<RispExp<T>>) -> Result<RispExp<T>, RispErr>,

我可以做Func(Box<Fn(&Vec<RispExp>) -> Result<RispExp, RispErr>>),但是dyn fn没有实现克隆。

您会建议我做什么?谢谢您的帮助,从一开始的锈学习者开始:}

1 个答案:

答案 0 :(得分:0)

如果要节省输入内容,请将您的type参数绑定为别名。

type RispFunc<T> = Fn(&Vec<RispExp<T>>) -> Result<RispExp<T>, RispErr>;

enum RispExp<Func: RispFunc<Func>> {
  ...
}

否则,如果您使用特征对象方法,则可以尝试实现自己的clone()-How to clone a struct storing a boxed trait object?