如何在Rust中创建盒装封口矢量?

问题描述 投票:6回答:2

以前a question被问及创建一个函数数组,其中函数从一个范围返回整数。最终的解决方案是做一张地图/收集到Vec<_>

我有一个类似但不同的情况,我有相同的签名但不同的实现闭包。我试过这个:

let xs: Vec<_> = vec![
    move |(x, y)| (y, x),
    move |(x, y)| (1 - y, 1 - x),
];

我得到的错误:

error[E0308]: mismatched types
 --> src/main.rs:4:9
  |
4 |         move |(x, y)| (1 - y, 1 - x),
  |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure
  |
  = note: expected type `[closure@src/main.rs:3:9: 3:29]`
             found type `[closure@src/main.rs:4:9: 4:37]`
  = note: no two closures, even if identical, have the same type
  = help: consider boxing your closure and/or using it as a trait object

我试过拳击:

let xs: Vec<_> = vec![
    Box::new(move |x: u8, y: u8| (y, x)),
    Box::new(move |x: u8, y: u8| (1 - y, 1 - x)),
];

我得到了同样的错误:

error[E0308]: mismatched types
 --> src/main.rs:4:18
  |
4 |         Box::new(move |x: u8, y: u8| (1 - y, 1 - x)),
  |                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected closure, found a different closure
  |
  = note: expected type `[closure@src/main.rs:3:18: 3:44]`
             found type `[closure@src/main.rs:4:18: 4:52]`
  = note: no two closures, even if identical, have the same type
  = help: consider boxing your closure and/or using it as a trait object

盒子闭合的正确方法是什么,以便它们可以放入矢量(或数组)?

rust closures boxing
2个回答
13
投票

问题是类型推断在你想要它之前已经开始了。从概念上讲,它是这样的:

let mut xs: Vec<_> = Vec::new();
xs.push(Type1);
xs.push(Type2);

当看到第一个值时,Vec元素的类型被推断为该类型。然后第二个元素导致不匹配。

即使你Box值,你有同样的问题:

let mut xs: Vec<_> = Vec::new();
xs.push(Box::new(Type1));
xs.push(Box::new(Type2));

从另一个角度来看,你从未真正创建过一个特质对象。你有一个Box<ConcreteType>,而不是Box<dyn Trait>

解决方案是将盒装混凝土类型转换为盒装特征对象:

let mut xs: Vec<_> = Vec::new();
xs.push(Box::new(Type1) as Box<dyn Trait>);
xs.push(Box::new(Type2) as Box<dyn Trait>);

第二个push可以自动强制类型,因此您可以选择将as位置离开该线。

回滚到原来的问题:

let xs: Vec<_> = vec![
    Box::new(move |(x, y)| (y, x)) as Box<dyn Fn((i32, i32)) -> (i32, i32)>,
    Box::new(move |(x, y)| (1 - y, 1 - x)),
];

或者你可以通过在变量上指定类型来避免推断,我的首选样式是:

let xs: Vec<Box<dyn Fn((i32, i32)) -> (i32, i32)>> = vec![
    Box::new(move |(x, y)| (y, x)),
    Box::new(move |(x, y)| (1 - y, 1 - x)),
];

3
投票

您应该将错误消息中的建议读作“考虑装箱闭包并将其用作特征对象,或者将其用作特征对象”。

使用trait对象引用而不用装箱它们在这里不起作用,因为没有任何东西拥有闭包。向量中的引用将比闭包更长时间:

// This fails
let xs: Vec<&Fn((i32, i32)) -> (i32, i32)> = vec![ 
    &move |(x, y)| (y, x), 
    &move |(x, y)| (1 - y, 1 - x),
];

向量需要获取闭包的所有权,这是特征对象发挥作用的地方:

let xs: Vec<Box<Fn((i32, i32)) -> (i32, i32)>> = vec![ 
    Box::new(move |(x, y)| (y, x)), 
    Box::new(move |(x, y)| (1 - y, 1 - x)),
];

这明确告诉编译器向量可以包含具有相同接口的任何闭包的框。

© www.soinside.com 2019 - 2024. All rights reserved.