首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >函数的锈蚀HashMap的类型签名

函数的锈蚀HashMap的类型签名
EN

Stack Overflow用户
提问于 2017-07-18 06:45:17
回答 2查看 1.2K关注 0票数 0

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

代码语言:javascript
复制
let functions: HashMap<_, _> = vec!(("+", Box::new(plus))).into_iter().collect();

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

代码语言:javascript
复制
let functions: HashMap<&str, Box<Fn(Vec<Expression>) -> Expression>> =
    vec!(("+", Box::new(plus))).into_iter().collect();

编译器错误消息没有多大帮助:

代码语言:javascript
复制
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的实际类型是什么?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2017-07-18 07:30:37

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

我希望plus被宣布为:

代码语言:javascript
复制
fn plus(v: Vec<Expression>) -> Expression;

在本例中,plus的类型是fn(Vec<Expression>) -> Expression {plus},实际上是一个伏地魔型:它不能命名。

最值得注意的是,它不同于最终的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>是一个盒式特性对象,通常意味着一个虚拟指针和一个数据指针。

因此,类型归属不起作用,因为它只能执行免费的矫顽力。

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

代码语言:javascript
复制
// 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();
               ^~~~~~~~~~~~~~~~~~~~~~~~

或者,您可能更愿意保留未装箱的函数:

代码语言:javascript
复制
// Simple functions only
type FnExpr = fn(Vec<Expression>) -> Expression;

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

Stack Overflow用户

发布于 2017-07-18 07:32:47

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

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

下面的代码给出编译错误

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

这是可行的,但没有用

代码语言:javascript
复制
let functions: HashMap<_, _> =
    vec![("+", Box::new(plus)), 
         ("-", Box::new(plus))].into_iter().collect();

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

代码语言:javascript
复制
type BoxedFn = Box<Fn(Vec<Expression>) -> Expression>;

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

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

代码语言:javascript
复制
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();
票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/45159414

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档