编者注:此代码示例来自1.0之前的Rust版本,并且在语法上不是有效的Rust 1.0代码。此代码的更新版本会产生不同的错误,但答案仍包含有价值的信息。
无论是否有Box
,无论是否有生命,我都尝试过这个:
trait TraitToImpl {
fn do_something(self, val: i32);
}
struct Cont {
value: i32,
}
impl TraitToImpl for Cont {
fn do_something(self, val: i32) {
println!("{}", val);
}
}
struct StoreTrait<'a> {
field: Box<TraitToImpl + 'a>,
}
fn main() {
let cont = Box::new(Cont { value: 12 }) as Box<TraitToImpl>;
let a = StoreTrait { field: cont };
a.field.do_something(123);
}
我得到的只是这个错误:
error: cannot convert to a trait object because trait `TraitToImpl` is not object-safe
答案 0 :(得分:8)
问题是,正如错误消息所说,特征TraitToImpl
不是对象安全的。也就是说,通过引用(即&TraitToImpl
或Box<TraitToImpl>
使用该特定特征不安全。
具体而言,do_something
方法按值self
获取。考虑一下:编译器如何<{>>调用这个方法Cont
放在Box<TraitToImpl>
?它必须将值复制到Cont
类型的参数中(这是impl
所期望的),但此调用必须适用于任何类型的任何类型大小可能会实现TraitToImpl
!
实际结果:如果您的特征包含按值self
或泛型,则不能通过引用使用它。在调用发生时,编译器不再具有足够的信息来实际生成必要的代码。
也许尝试取代&self
? :)
答案 1 :(得分:3)
要了解此错误的来源,也可以在Object Safety RFC中查找 object-safe 的定义:
我们说特征
m
上的方法T
是对象安全的,如果它是合法的(在当前的Rust中)来调用x.m(...)
x
类型{{1} },即&T
是一个特质对象。如果x
中的所有方法都是对象安全的,那么我们说T
是对象安全的。
正如@DK所指出的那样,T
是非法的,因为x.do_something(...)
无法通过值传递。
请注意,通过从非对象安全特征派生,也可以违反对象安全性。 E.g。
TraitToImpl
不会是对象安全的,因为trait TraitToImpl : Clone {
fn do_something(&self, val: int); // this method is object-safe
}
本身有一个非对象安全的方法(Clone
)。