Here's a contrived example of what i'm trying to achieve.
trait AwesomeTrait {
fn awesomeness (&self) {
println!("Awesome Trait!");
}
}
struct AwesomeStruct;
impl AwesomeTrait for AwesomeStruct {}
struct AwesomeStruct2;
impl AwesomeTrait for AwesomeStruct2 {}
struct ListOfAwesomeStruct<S>
where S: AwesomeTrait {
list: Vec<S>
}
impl<S: AwesomeTrait> ListOfAwesomeStruct<S> {
fn new () -> Self {
Self {
list: Vec::new(),
}
}
fn push(&mut self, awesome: S) -> &mut Self {
self.list.push(awesome);
self
}
}
fn main() {
let ooo = ListOfAwesomeStruct::new();
ooo.push(AwesomeStruct);
ooo.push(AwesomeStruct2); /// cannot execute this push because `ooo` is now of type `ListOfAwesomeStruct<AwesomeStruct>`
}
errors
error[E0308]: mismatched types
--> src/main.rs:37:14
|
37 | ooo.push(AwesomeStruct2);
| ^^^^^^^^^^^^^^ expected struct `AwesomeStruct`, found struct `AwesomeStruct2`
|
= note: expected type `AwesomeStruct`
found type `AwesomeStruct2`
error: aborting due to previous error
error: Could not compile `playground`.
To learn more, run the command again with --verbose.
You need to store trait objects (i.e. erased AwesomeTrait values). You can also make caller give you a Box<AwesomeTrait> instead of having the generic function there - I left the generic version though.
Signature of clone is fn clone(&self) -> Self, that is compiler must know the size of type which implements Clone to be able to allocate stack space for the returned value. Size of a trait object is unknown at compile time, thus Clone can't be implemented for trait objects.
You can mitigate this by adding your own version of the clone function like this.
This works, unlike implementing Clone for AwesomeTrait directly, because you are manipulating Boxes all the time and those have a well-defined size. The compiler can thus stack-allocate the space for the cloned box.
@seunlanlege, what's the reason you need Clone here? Do you actually need/want to clone the trait object or are you trying to bypass lifetime issues? If it's the latter, you can put your trait objects inside Rc (or Arc if you need thread-safety) and then have a refcounted pointer to the single instance of each of them. Note that if you did go with Rc/Arc and needed to borrow the trait object mutably (i.e. &mut self), you'd need to wrap the trait object in a RefCell in addition (i.e. Rc<RefCell<AwesomeTrait>>).
I need multiple owners of a struct that holds trait objects in a multithread context, (that was a mouthful). I initially tried using Arc, but Arc moves the value. so no Arc didn't work for me.
impl ArbitraryTrait for ListOfAwesomeStruct {}
fn contrived<S>(structOfTraitObjects: S)
where
S: ArbitraryTrait + Send + Sync + 'static
{
let structOfTraitObjects = Arc::new(structOfTraitObjects);
for _ in 1..4 {
thread::spawn(move || {
funcThatExpectsArbitraryTraitObject(structOfTraitObjects.clone());
// can't do this, the first iteration of thread::spawn moves structOfTraitObjects
}
}
}
where i could just do
impl ArbitraryTrait for ListOfAwesomeStruct {}
fn contrived<S>(structOfTraitObjects: S)
where
S: ArbitraryTrait + Send + Sync + Clone + 'static
{
let structOfTraitObjects = structOfTraitObjects.clone();
for _ in 1..4 {
thread::spawn(move || {
funcThatExpectsArbitraryTraitObject(structOfTraitObjects.clone());
// Awwww, yeaaahhhh!
}
}
}
It is the closure which moves structOfTraitObjects , not Arc.
This will work
let struct_of_trait_objects = Arc::new(struct_of_trait_objects);
for _ in 1..4 {
let cloned_arc = struct_of_trait_objects.clone();
thread::spawn(move || {
funcThatExpectsArbitraryTraitObject(cloned_arc);
...