我创建了一个 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
的实际类型是什么? ?
最佳答案
如果您仔细观察差异,您就会找到答案,尽管这可能令人费解。
我希望 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>
是一个装箱的 trait 对象,通常表示虚拟指针和数据指针。
因此类型归属不起作用,因为它只能执行无成本的强制转换。
解决方案是在为时已晚之前执行转换:
// 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();
关于types - 函数的 Rust HashMap 的类型签名,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45159414/