【问题标题】:Sending Vec<Box<Trait>> over channel通过通道发送 Vec<Box<Trait>>
【发布时间】:2018-06-12 19:48:46
【问题描述】:

我正在尝试通过频道发送Vec&lt;Box&lt;Trait&gt;&gt;。我猜,发送部分有点用。在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 中获取内部价值?

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

【问题讨论】:

标签: rust


【解决方案1】:

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

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

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

  • something 的类型为:Something
  • something.list 的类型为:Vec&lt;Box&lt;AwesomeTrait + Send&gt;&gt;
  • somethings.list.iter() 属于 std::slice::Iter&lt;...&gt; 类型(不重要)。
  • s 的类型为 &amp;Box&lt;AwesomeTrait + Send&gt;。请务必注意,它是一个框的引用,因为您使用的是iter() 而不是into_iter()

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

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

生成的代码将是:

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

或者如果你愿意消费这个列表:

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

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

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&amp;AwesomeTrait 的类型当然符合条件。但是一个 trait 是一个无大小的类型 (DST),所有泛型函数类型参数默认都需要一个 Sized 类型。

解决方案是在K::abc 中添加?Sized no-requirement:

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

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

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

【讨论】:

  • 嘿,我的答案中缺少 ?Sized 界限,感谢您提供!
  • 哇,谢谢你的精彩解释,它很有效,很有意义!出于好奇,这是在使用 Rust 较长时间后自然而然出现的吗?似乎很难记住所有这些事情。感觉就像我必须检查我想使用的每个第二个函数的文档。
  • @Andrew:是的,我已经为 Rust 苦苦挣扎了一段时间,但有一天,它在我脑海中突然出现,突然一切都变得有意义了。现在,当我用任何其他语言编程时,我发现它们笨拙且不可靠;-)。请注意,我仍然必须在每个第二个函数中检查 stdlib 文档,但现在我读到的内容对我来说完全有意义。
【解决方案2】:

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

K::abc(&*something)

但是等等! iter() 不消耗Vec&lt;Box&lt;Trait&gt;&gt; 的所有权,因此每个元素都是&amp;Box&lt;Trait&gt; 类型。为了解决这个问题,我们需要调用into_iter()

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

【讨论】:

  • 谢谢,我刚刚尝试过,但它似乎仍然不起作用。 AwesomeTrait + std::marker::Send does not have a constant size known at compile-time.
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-03-29
  • 2015-05-20
  • 2018-08-13
  • 1970-01-01
  • 1970-01-01
  • 2014-09-25
  • 1970-01-01
相关资源
最近更新 更多