如何在 Rust 中创建盒装闭包向量?
How to create a vector of boxed closures in Rust?
之前 a question 被问及创建一个函数数组,其中函数返回一个范围内的整数。最终的解决方案是将 map/collect 变成 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
盒子闭合的正确方法是什么,以便它们可以放入向量(或数组)中?
您应该阅读错误消息中的建议 "consider boxing your closure and using it as a trait object, or using it just as a trait object"。
在不装箱的情况下使用特征对象引用在这里不起作用,因为没有任何东西拥有闭包。向量中的引用将比闭包更有效:
// This fails
let xs: Vec<&Fn((i32, i32)) -> (i32, i32)> = vec![
&move |(x, y)| (y, x),
&move |(x, y)| (1 - y, 1 - x),
];
vector 需要获取闭包的所有权,这就是装箱 trait 对象发挥作用的地方:
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)),
];
这明确告诉编译器向量可以包含具有相同接口的任何闭包的框。
问题是类型推断在您希望它启动之前就启动了。从概念上讲,它是这样的:
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>);
second 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)),
];
之前 a question 被问及创建一个函数数组,其中函数返回一个范围内的整数。最终的解决方案是将 map/collect 变成 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
盒子闭合的正确方法是什么,以便它们可以放入向量(或数组)中?
您应该阅读错误消息中的建议 "consider boxing your closure and using it as a trait object, or using it just as a trait object"。
在不装箱的情况下使用特征对象引用在这里不起作用,因为没有任何东西拥有闭包。向量中的引用将比闭包更有效:
// This fails
let xs: Vec<&Fn((i32, i32)) -> (i32, i32)> = vec![
&move |(x, y)| (y, x),
&move |(x, y)| (1 - y, 1 - x),
];
vector 需要获取闭包的所有权,这就是装箱 trait 对象发挥作用的地方:
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)),
];
这明确告诉编译器向量可以包含具有相同接口的任何闭包的框。
问题是类型推断在您希望它启动之前就启动了。从概念上讲,它是这样的:
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>);
second 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)),
];