2017-07-18 104 views
0

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

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

如果我让拉斯特推断类型对我来说,在上面的代码,它编译并运行正常,因为在上面的代码。但是,如果我尝试指定类型,它不会编译:

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的?

+0

看来我的谷歌福是真的很弱,我敢发誓,有这种重复:( –

回答

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 ...的转换需要存储器表示的变化。这是因为:

  • 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(); 
+0

顺便说一句,'FN(A) - > B {加}'是不是指针[它是零大小。](https://play.rust-lang.org/?gist=0ad4ee6d789294e3af47d0a71dc74940&version=stable) – red75prime

+0

@ red75prime:哦,对,我总是忘记这件整洁的东西! –

+0

感谢您的详细和全面的答案这完全回答了我的问题。 – isaacg

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();