4

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();
    }
}
Andrew
  • 2,063
  • 3
  • 24
  • 40
  • I believe your first question is answered by the question and answers of [When should I not implement a trait for references to implementors of that trait?](https://stackoverflow.com/q/28799372/155423). If you disagree, please [edit] your question to explain the differences. Otherwise, we can mark this question as already answered. – Shepmaster Jun 12 '18 at 19:59
  • Thanks, the second question is solved by your first comment! I'll edit out that part of the question, but I'm still not sure how to solve the first part, even with that suggestion you linked. – Andrew Jun 12 '18 at 20:12

2 Answers2

3

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.

rodrigo
  • 94,151
  • 12
  • 143
  • 190
  • 1
    Heh, I was missing the ?Sized bound on my answer, thanks for including that! – Ki Chjang Jun 12 '18 at 21:40
  • Wow, thank you for the awesome explanation, it works and makes a lot of sense! Out of curiosity, is this something that comes naturally after using Rust for a longer period of time? It seems kinda hard to remember all these things. It feels like I have to check the docs for every 2nd function I would like to use. – Andrew Jun 12 '18 at 23:34
  • 1
    @Andrew: Yes, I've struggled for some time with Rust, but then, one day it clicked into place in my head and suddenly it all made sense. Now when I program in any other language I find them clumsy and unreliable ;-). Mind you, I still have to check the stdlib docs every 2nd function, but now what I read totally makes sense to me. – rodrigo Jun 12 '18 at 23:38
0

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);
}
Ki Chjang
  • 311
  • 3
  • 9
  • Thanks, I've just tried this, but it still doesn't seem to work. `AwesomeTrait + std::marker::Send does not have a constant size known at compile-time`. – Andrew Jun 12 '18 at 21:02