通过trait在struct中存储一个值是否可行?

7

编辑注:此代码示例来自 Rust 1.0 之前的版本,不是 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);
}

我只得到了这个错误信息:
错误:无法转换为Trait对象,因为TraitToImpl特性不是对象安全的。
2个回答

8
问题在于,正如错误信息所说,TraitToImpl 特质不是对象安全的。也就是说,通过引用(如 &TraitToImplBox<TraitToImpl>)使用该特定特质是不安全的。
具体来说,do_something 方法按值获取 self。考虑一下:编译器如何在将 Cont 放入 Box<TraitToImpl> 中后调用此方法?它必须将该值复制到类型为 Cont 的参数中(这是 impl 所期望的),但是这个调用必须适用于可能实现 TraitToImpl 的任何类型和任何大小!
实际结果是:如果您有一个包含按值 self 或泛型的特质,则无法通过引用使用它。在调用发生的时候,编译器不再具有足够的信息来生成必要的代码。
也许可以尝试使用 &self 代替? :)

非常感谢。这个错误信息对我来说不够清晰。而且,如果我理解正确的话,通过 trait 传递按值传递是不可能的。 - Sergey Zalizko

3
为了理解这个错误的源头,查看Object Safety RFCobject-safe的定义可能会有所帮助:

我们说trait T 上的方法m是对象安全的,如果在当前Rust中调用x.m(...)是合法的,其中x具有类型&T,即x是一个trait对象。如果T中的所有方法都是对象安全的,则我们说T是对象安全的。

正如@DK指出的那样,x.do_something(...)是非法的,因为无法通过值传递TraitToImpl
请注意,从非对象安全trait派生也可能违反对象安全性。例如:
trait TraitToImpl : Clone {
    fn do_something(&self, val: int); // this method is object-safe
}

由于Clone本身具有非对象安全方法(fn clone(&self) -> Self),因此不符合对象安全要求。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接