发送Vec <box <trait>&gt;过度通道

时间:2018-06-12 19:48:47

标签: rust

我试图通过频道发送Vec<Box<Trait>>。我想,发送部分有点作品。 recv() Vec之后{I}尝试迭代它并将内部值的引用传递给函数,该函数失败并显示错误:

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中获取内部价值?

Here's a minimal reproduction.

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();
    }
}

2 个答案:

答案 0 :(得分:3)

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

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

但是**s属于AwesomeTrait类型,您需要对其进行引用,因此您必须获取&**s的地址,其类型为&AwesomeTrait

结果代码为:

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

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

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

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

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

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

,可以Vec
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类型。

解决方案是向?Sized添加K::abc无要求:

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

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

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

答案 1 :(得分:0)

您很可能希望取消引用Box<Trait>以取消Trait,但这显然是一种未经过尺寸调整的类型,因此您需要立即制作像这样引用它:

K::abc(&*something)

但是等等! iter()不会消耗Vec<Box<Trait>>的所有权,因此每个元素都属于&Box<Trait>类型。要解决此问题,我们需要拨打into_iter()来代替:

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