我创建了一个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
的实际类型是什么?
答案 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();