Trait Object不是对象安全错误

时间:2015-05-01 10:20:11

标签: rust

以下代码不能为我编译。

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(&self)进入图片时出现问题。如果fun0是特征中唯一存在的函数,它将编译并运行正常。但是我的代码需要这样的模式 - 我该怎么做?

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

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

现在的问题是什么?

2 个答案:

答案 0 :(得分:8)

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

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

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

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

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

此类方法(无法处理特征对象)被称为对象不安全(或非对象安全)。如果特征包含这样的方法,它就不能成为特征对象 - 它也被称为不是对象安全的。

我认为,有效的是,你可以让特质返回一个特质对象:

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

现在取消对实际类型的依赖,并且该特征再次成为对象安全。

答案 1 :(得分:2)

嗯,错误信息几乎说明了当前的问题:您正在尝试在对象上下文中使用非对象安全特征,而您无法做到这一点。

您可以从特征fun2中删除A,并将其定义为不同的特征:它的存在将阻止A被用作&#34;特征对象&# 34 ;;所以&ABox<A>等等都是不可能的。然后,每种类型都可以实现这两种特征。

另一种方法是更改​​fun2,使其结果包含Self类型;你的例子太抽象而不知道,但Option<Box<A>>会被接受吗?

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