通过通道发送 Vec<Box<Trait>>

  • 本文关键字:Trait Box Vec 通道 rust
  • 更新时间 :
  • 英文 :


我正在尝试通过通道发送Vec<Box<Trait>>。发送部分有点工作,我猜。recv()Vec后,我尝试迭代它并将内部值的引用传递给一个函数,该函数失败并显示错误:

error[E0277]: the trait bound `&std::boxed::Box<AwesomeTrait + std::marker::Send>: AwesomeTrait` is not satisfied
--> src/main.rs:12:13
|
12 |             K::abc(&something);
|             ^^^^^^ the trait `AwesomeTrait` is not implemented for `&std::boxed::Box<AwesomeTrait + std::marker::Send>`
|
note: required by `K::abc`
--> src/main.rs:57:5
|
57 |     pub fn abc<T: AwesomeTrait>(something: &T) {
|     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

有没有办法以某种方式从Box中获得内在价值?

这是一个最小的复制品。

use std::sync::mpsc;
use std::thread;
fn main() {
let (tx, rx) = mpsc::channel::<Request>();
let s = Something::new();
tx.send(Request::Do(s)).unwrap();
let z = thread::spawn(move || match rx.recv().unwrap() {
Request::Do(somethings) => for something in somethings.list.iter() {
K::abc(&something);
},
});
z.join();
}
pub enum Request {
Do(Something),
}
pub struct Something {
list: Vec<Box<AwesomeTrait + Send>>,
}
impl Something {
pub fn new() -> Self {
Self { list: Vec::new() }
}
pub fn from<T: AwesomeTrait + Send + 'static>(something: T) -> Self {
let mut list = Vec::with_capacity(1);
list.push(Box::new(something));
// Self { list }
Self { list: Vec::new() }
}
pub fn push<T: AwesomeTrait + Send + 'static>(&mut self, something: T) {
self.list.push(Box::new(something));
}
}
pub trait AwesomeTrait {
fn func(&self);
}
pub struct X {}
impl AwesomeTrait for X {
fn func(&self) {}
}
pub struct K {}
impl K {
pub fn abc<T: AwesomeTrait>(something: &T) {
&something.func();
}
}

让我评论一下这个表达式的类型:

for s in somethings.list.iter() {
K::abc(&s);
}

(我重命名了迭代器变量,以避免混淆(。

  • something的类型为:Something
  • something.list的类型是:Vec<Box<AwesomeTrait + Send>>
  • somethings.list.iter()属于std::slice::Iter<...>类型(不重要(。
  • s属于&Box<AwesomeTrait + Send>型。重要的是要注意它是对框的引用,因为您使用的是iter()而不是into_iter()

要获得实际AwesomeTrait您需要取消引用s以获取Box,然后再次取消引用以获取内部对象:**s

但是**sAwesomeTrait型的,你需要一个引用,所以你必须用&**s获取地址,它是&AwesomeTrait型的

。生成的代码将是:

for s in somethings.list.iter() {
K::abc(&**s);
}

或者,如果您愿意使用该列表:

for s in somethings.list.into_iter() {
K::abc(&*s);
}

如果您不想考虑使用多少*,则可以使用由Box实现的AsRef特征,并信任编译器的自动引用:

for s in somethings.list.iter() {
K::abc(s.as_ref());
}

注意:.into_iter()也可以省略。如果您迭代对Vec的引用,可以.iter()

for s in somethings.list { //consume the list
K::abc(&*s);
}

或:

for s in &somethings.list { //do not consume the list
K::abc(&**s);
}

你认为你已经完成了,但还没有...此代码引发以下编译器错误:

error[E0277]: the trait bound `AwesomeTrait + std::marker::Send: std::marker::Sized` is not satisfied
--> src/main.rs:12:13
|
12 |             K::abc(&**s);
|             ^^^^^^ `AwesomeTrait + std::marker::Send` does not have a constant size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `AwesomeTrait + std::marker::Send`
note: required by `K::abc`
--> src/main.rs:57:5
|
57 |     pub fn abc<T: AwesomeTrait>(something: &T) {
|     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

为什么?好吧,您的K::abc需要引用实现AwesomeTrait并且&AwesomeTrait肯定符合条件的类型。但特征是未调整大小的类型 (DST(,默认情况下,所有泛型函数类型参数都需要Sized类型。

解决方案是在K::abc中添加一个?Sized无要求:

impl K {
pub fn abc<T: AwesomeTrait + ?Sized>(something: &T) {
something.func();
}
}

(你在这个函数中有一个&什么都不做,我已经删除了它(。

?Sized的限制是你不能声明类型为T的变量或参数,只能声明&T&mut TBox<T>...但是您当前的代码没有任何禁止,所以没问题。

您很可能希望取消引用Box<Trait>以恢复Trait,但这显然是一个未调整大小的类型,因此您需要立即对其进行引用,如下所示:

K::abc(&*something)

但是等等!iter()不消耗Vec<Box<Trait>>的所有权,所以每个元素都是&Box<Trait>类型。要解决此问题,我们需要改为调用into_iter()

for something in somethings.list.into_iter() {
K::abc(&*something);
}

最新更新