Reputation: 2173
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
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
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