Andrew
Andrew

Reputation: 2173

Sending Vec<Box<Trait>> over channel

I'm trying to send Vec<Box<Trait>> over a channel. The sending part sort of works, I guess. After recv()ing the Vec I'm trying to iterate over it and pass the inner value's reference to a function, which fails with an error:

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) {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Is there a way to get the inner value out of the Box somehow?

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

Upvotes: 4

Views: 1405

Answers (2)

rodrigo
rodrigo

Reputation: 98398

Let me comment on the types of this expression:

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

(I've renamed the iterator variable, to avoid confussion).

  • something is of type: Something.
  • something.list is of type: Vec<Box<AwesomeTrait + Send>>.
  • somethings.list.iter() is of type std::slice::Iter<...> (not important).
  • s is of type &Box<AwesomeTrait + Send>. It is important to note that it is a reference to a box, because you are using iter() instead of into_iter().

To get the actual AwesomeTrait you need to dereference s to get the Box, and then dereference again to get to the inner object: **s.

But **s is of type AwesomeTrait, and you need a reference to that, so you must take get the address with &**s, which is of type &AwesomeTrait.

The resulting code will be:

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

Or if you are willing to consume the list:

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

If you don't want to think about how many * to use you can use the AsRef trait, implemented by Box, and trust the autoreferencing by the compiler:

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

Note: The .into_iter() can also be ellided. As can .iter() if you iterate a reference to the Vec:

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

or:

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

You think you are done, but not yet... this code throws this compiler error:

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) {
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Why is that? Well, your K::abc requires a reference to a type that implements AwesomeTrait and &AwesomeTrait certainly qualifies. But a trait is an unsized type (DST), and all generic function type parameters require a Sized type by default.

The solution is to add a ?Sized no-requirement to K::abc:

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

(You have an & in this function that does nothing, I've removed it).

The limitation with that ?Sized is that you cannot declare variables or parameters of type T, only of &T, &mut T, Box<T>... but your current code does nothing forbidden, so no problem.

Upvotes: 3

Ki Chjang
Ki Chjang

Reputation: 311

You'll most likely want to dereference the Box<Trait> in order to get back out a Trait, but this is obviously an unsized type, so you'll immediately need to make a reference out of it like so:

K::abc(&*something)

But wait! iter() does not consume ownership of the Vec<Box<Trait>>, and so every element is of type &Box<Trait>. To solve this, we'll need to call into_iter() instead:

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

Upvotes: 0

Related Questions