【问题标题】:Trait Object is not Object-safe error特征对象不是对象安全错误
【发布时间】:2015-05-01 20:06:42
【问题描述】:

以下代码无法为我编译。

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);
}

给出一个错误:

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`.

合作

rustc --version
rustc 1.0.0-nightly (00978a987 2015-04-18) (built 2015-04-19)

fun2(&amp;self)带入图片时出现问题。如果 fun0 是 trait 中唯一存在的函数,它编译并运行良好。但是我的代码需要这样的模式——我该怎么做?

编辑:此处已给出上述正确答案 (https://stackoverflow.com/a/29985438/1060004)。但是如果我从函数签名中删除&amp;self(即,使其成为静态),我会遇到同样的问题:

fn fun2() -> Option<Box<A>>

现在有什么问题?

【问题讨论】:

    标签: rust


    【解决方案1】:

    正如您所注意到的,当您删除 fun2 方法时,问题就消失了。让我们更仔细地看一下:

    fn fun2(&self) -> Option<Box<Self>>;
    

    请注意,它的输出类型包含Self,即实现特征的类型。例如,如果为String 实现了A,则为String

    impl A for String {
        fn fun2(&self) -> Option<Box<String>> { ... }
    }
    

    但是!使用 trait 对象,值的实际类型会被删除,我们唯一知道 trait 对象是它是实现 trait 的值,但我们不知道 实际类型该特征被实施。 Trait 对象方法是动态分派的,因此程序选择在运行时调用的实际方法。这些方法必须具有相同的行为,即接受相同数量的相同大小(成对)的参数并返回相同大小的值。如果一个方法在其签名中的某处使用Self,例如fun2,那么它的实现将不会相互兼容,因为它们需要对不同大小的值进行操作,因此这些方法无法统一。

    此类方法(不能与 trait 对象一起使用)称为对象不安全(或非对象安全)。如果一个 trait 包含这样的方法,它就不能成为一个 trait 对象——它也被称为非对象安全的。

    我相信,你可以让 trait 返回一个 trait 对象:

    fn fun2(&self) -> Option<Box<A>>
    

    现在对实际类型的依赖被解除,特征再次变为对象安全。

    【讨论】:

    • 15 秒。你真是个老鼠屎。 :D
    • 是的,它也经常发生在我身上:)
    • 一如既往的精彩!我试图将 Box 等同于 std::unique_ptr&lt;A&gt;,其中 A 是某个基类。这就是我虽然 Box 所做的,并且如果给出具体类型,则会发生隐式转换(到基础)。但似乎 Box 和 Box 是不同的概念。仍然把我的头缠在铁锈上:)
    • 是的,Rust 中(几乎)没有子类型,所以Box&lt;Self&gt; 永远不是Box&lt;A&gt;。你必须明确:)
    • 再问一个问题:如果方法签名是fn fun2() -&gt; Option&lt;Box&lt;A&gt;&gt;,那么它仍然是同样的错误。为什么?为什么静态函数不起作用?
    【解决方案2】:

    嗯,错误消息几乎说明了当前的问题:您试图在对象上下文中使用非对象安全特征,但您不能这样做。

    您可以从 trait A 中删除 fun2,并在不同的 trait 中定义它:它的存在将防止 A 被用作“特征对象”;所以&amp;ABox&lt;A&gt; 等都是不可能的。然后,每种类型都可以实现这两个特征。

    另一种选择是更改fun2,使其结果包含Self类型;你的例子太抽象了,不知道,但Option&lt;Box&lt;A&gt;&gt; 可以接受吗?

    至于为什么:为了将 trait 用作 trait 对象,编译器必须能够为 trait 的方法生成 vtable,以便它可以进行动态的运行时调度.这意味着特征中的每个方法都必须可以用相同的类型实现(self 参数是一个特例)。那么fun2 返回什么?它必须返回一个Option&lt;Box&lt;Self&gt;&gt;,但Self 对于每个实现都是不同的类型!没办法统一!

    【讨论】:

      猜你喜欢
      • 2017-10-21
      • 2018-11-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-10-23
      • 2017-10-25
      相关资源
      最近更新 更多