键入函数的Rust HashMap的签名

时间:2017-07-18 06:45:18

标签: types compiler-errors rust

我创建了一个HashMap,它将字符串映射到Vec<Expression> -> Expression类型的函数,其中Expression是我定义的类型。有问题的代码是:

let functions: HashMap<_, _> = vec!(("+", Box::new(plus))).into_iter().collect();

如果我让Rust为我推断类型,就像上面的代码一样,它编译并运行正常,如上面的代码所示。但是,如果我尝试指定类型,它就不会编译:

let functions: HashMap<&str, Box<Fn(Vec<Expression>) -> Expression>> =
    vec!(("+", Box::new(plus))).into_iter().collect();

编译器错误消息非常有用:

let functions: HashMap<&str, Box<Fn(Vec<Expression>) -> Expression>> = vec!(("+", Box::new(plus))).into_iter().collect();
^^^^^^^ a collection of type `std::collections::HashMap<&str, std::boxed::Box<std::ops::Fn(std::vec::Vec<Expression>) -> Expression>>` cannot be built from an iterator over elements of type `(&str, std::boxed::Box<fn(std::vec::Vec<Expression>) -> Expression {plus}>)`

HashMap的实际类型是什么?

2 个答案:

答案 0 :(得分:2)

错误消息的相关部分是Box<std::ops::Fn ... >Box<fn ... {plus}>。第一个是盒装Fn特征对象。第二个是盒装函数plus。请注意,它不是指向函数的盒装指针,它将是Box<fn ...>而没有{plus}部分。它是函数plus本身的唯一且不可命名的类型。

那就是你不能写出这个HashMap的真实类型,因为它包含的类型是不可命名的。但这不是什么大问题,你只能将plus函数放入其中。

以下代码给出了编译错误

let functions: HashMap<_, _> =
    vec![("+", Box::new(plus)), 
         ("-", Box::new(minus))].into_iter().collect();
                        ^^^^^ expected fn item, found a different fn item

这有效,但没用了

let functions: HashMap<_, _> =
    vec![("+", Box::new(plus)), 
         ("-", Box::new(plus))].into_iter().collect();

一种可能的解决方案是将矢量的第一个元素转换为所需的类型。

type BoxedFn = Box<Fn(Vec<Expression>) -> Expression>;

let functions: HashMap<&str, BoxedFn> =
    vec![("+", Box::new(plus) as BoxedFn),
         ("_", Box::new(minus))].into_iter().collect();

另一个是中间变量的类型归属。

type BoxedFn = Box<Fn(Vec<Expression>) -> Expression>;

let v: Vec<(_, BoxedFn)> = vec![("+", Box::new(plus)), ("_", Box::new(minus))];
let functions: HashMap<&str, BoxedFn> = v.into_iter().collect();

答案 1 :(得分:1)

如果仔细观察差异,你会得到答案,虽然这可能令人费解。

我希望plus已声明为:

fn plus(v: Vec<Expression>) -> Expression;

在这种情况下,plus的类型为fn(Vec<Expression>) -> Expression {plus},实际上是Voldemort Type:无法命名。

最值得注意的是,它与最终的fn(Vec<Expression>) -> Expression {multiply}不同。

这两种类型可以强制为fn(Vec<Expression>) -> Expression {没有{plus} / {multiply}面额。

后一种类型可以转换为Fn(Vec<Expression>) -> Expression,这是任何不会修改其环境的可调用的特征(例如闭包|v: Vec<Expression>| v[0].clone())。

问题是,虽然fn(a) -> b {plus}可以转换为fn(a) -> b,但可以转换为Fn(a) -> b ...转换需要更改内存表示< / em>的。这是因为:

  • fn(a) -> b {plus}是零大小的类型,
  • fn(a) -> b是指向函数的指针
  • Box<Fn(a) -> b>是一个盒装的特征对象,通常表示虚拟指针都是数据指针。

因此类型归属不起作用,因为它只能执行无成本的强制。

解决方案是在为时已晚之前进行转换:

// Not strictly necessary, but it does make code shorter.
type FnExpr = Box<Fn(Vec<Expression>) -> Expression>;

let functions: HashMap<_, _> =
    vec!(("+", Box::new(plus) as FnExpr)).into_iter().collect();
               ^~~~~~~~~~~~~~~~~~~~~~~~

或者您可能更喜欢保留未装箱的功能:

// Simple functions only
type FnExpr = fn(Vec<Expression>) -> Expression;

let functions: HashMap<_, _> =
    vec!(("+", plus as FnExpr)).into_iter().collect();