Reputation: 3760
The following code does not compile for me.
trait A {
fn fun0(&self);
fn fun2(&self) -> Option<Box<Self>>;
}
struct B0 {
id: usize,
}
impl A for B0 {
fn fun0(&self) { println!("Value: {:?}", self.id); }
fn fun2(&self) -> Option<Box<Self>> { Option::None }
}
struct B1 {
id: isize,
}
impl A for B1 {
fn fun0(&self) { println!("Value: {:?}", self.id); }
fn fun2(&self) -> Option<Box<Self>> { Option::Some(Box::new(B1 { id: self.id, })) }
}
enum C {
None,
Put { object: Box<A>, },
}
fn fun1(values: Vec<C>) {
for it in values.iter() {
match *it {
C::Put { object: ref val, } => val.fun0(),
C::None => (),
};
}
}
fn main() {
let obj_b0 = Box::new(B0 { id: 778, });
let obj_b1 = Box::new(B1 { id: -8778, });
let obj_c0 = C::Put { object: obj_b0, };
let obj_c1 = C::Put { object: obj_b1, };
let mut vec = Vec::new();
vec.push(obj_c0);
vec.push(obj_c1);
fun1(vec);
}
gives an error:
cargo run
Compiling misc v0.0.1 (file:///home/spandan/virtualization/coding/my/rust-tests/misc/misc)
src/main.rs:188:48: 188:54 error: the trait `A` is not implemented for the type `A` [E0277]
src/main.rs:188 C::Put { object: ref val, } => val.fun0(),
^~~~~~
src/main.rs:197:35: 197:41 error: cannot convert to a trait object because trait `A` is not object-safe [E0038]
src/main.rs:197 let obj_c0 = C::Put { object: obj_b0, };
^~~~~~
src/main.rs:197:35: 197:41 note: method `fun2` references the `Self` type in its arguments or return type
src/main.rs:197 let obj_c0 = C::Put { object: obj_b0, };
^~~~~~
src/main.rs:198:35: 198:41 error: cannot convert to a trait object because trait `A` is not object-safe [E0038]
src/main.rs:198 let obj_c1 = C::Put { object: obj_b1, };
^~~~~~
src/main.rs:198:35: 198:41 note: method `fun2` references the `Self` type in its arguments or return type
src/main.rs:198 let obj_c1 = C::Put { object: obj_b1, };
^~~~~~
error: aborting due to 3 previous errors
Could not compile `misc`.
working with
rustc --version
rustc 1.0.0-nightly (00978a987 2015-04-18) (built 2015-04-19)
The problem appears when fun2(&self)
is brought into the picture. It compiles and runs fine if fun0
is the only existing function in the trait. But my code needs such a pattern - How do i do it ?
Edit: the correct answer for the above has been given here (https://stackoverflow.com/a/29985438/1060004) . But i am running into the same problem if i remove the &self
from function signature (ie., make it static):
fn fun2() -> Option<Box<A>>
what is the issue now ?
Upvotes: 9
Views: 3531
Reputation: 127781
As you have noticed, the problem vanishes when you remove fun2
method. Let's look at it more closely:
fn fun2(&self) -> Option<Box<Self>>;
Note that its output type contains Self
, that is, the type which the trait is implemented for. For example, if A
is implemented for String
, it would be String
:
impl A for String {
fn fun2(&self) -> Option<Box<String>> { ... }
}
However! With the trait objects the actual type of the value is erased, and the only thing that we know about trait objects is that it is a value which implements the trait, but we don't know the actual type the trait is implemented for. Trait object methods are dispatched dynamically, so the program selects the actual method to call at runtime. These methods have to behave identically, that is, accept the same number of parameters of the same size (pairwise) and return values of the same size too. If a method uses Self
somewhere in its signature, like fun2
, its implementations won't be compatible with each other because they would need to operate on values of different size, and hence such methods can't be unified.
Such methods (which can't work with trait objects) are called object-unsafe (or not object-safe). If a trait contains such methods, it can't be made a trait object - it is also called not object-safe.
What would work, I believe, is that you can make the trait return a trait object:
fn fun2(&self) -> Option<Box<A>>
Now the dependency on the actual type is lifted, and the trait becomes object-safe again.
Upvotes: 14
Reputation: 59005
Well, the error message pretty much spells out the immediate problem: you're trying to use a non-object-safe trait in an object context, and you can't do that.
You can remove fun2
from the trait A
, and define it in a different trait: its presence will prevent A
from ever being used as a "trait object"; so &A
, Box<A>
, etc. will all out of the question. Each type can then implement both of these traits.
Another alternative is to change fun2
so that its result does not contain the Self
type; your example is too abstract to know, but would Option<Box<A>>
be acceptable?
As for why: in order for a trait to be used as a trait object, the compiler has to be able to generate a vtable for the trait's methods so that it can do dynamic, runtime dispatch. This means that every method in the trait has to be implementable with the same types (the self
parameter is a special case). So what does fun2
return? It has to return an Option<Box<Self>>
, but Self
is a different type for every implementation! There's no possible way to unify it!
Upvotes: 2