特征 `x` 没有为类型 `x` 实现

Trait `x` is not implemented for the type `x`

编译以下代码时:

trait RenderTarget {}

struct RenderWindow;
impl RenderTarget for RenderWindow {}

trait Drawable {
    fn draw<RT: RenderTarget>(&self, target: &mut RT);
}

fn main() {
    let mut win = RenderWindow;
    let mut vec: Vec<Box<Drawable>> = Vec::new();

    for e in &vec {
        e.draw(&mut win);
    }
}

我收到错误:

error: the trait `Drawable` is not implemented for the type `Drawable` [E0277]
src/main.rs:15         e.draw(&mut win);
                         ^~~~~~~~~~~~~~

错误消息试图说明什么?另外,如何解决?

有一个,但那里的解决方案是修改特征A(在我的情况下对应于Drawable),但这在这里是不可能的,因为Drawable 来自外部图书馆。

更新: 将对象安全规则固定为 1.0 版本。即,按值 self 不再使方法对象不安全。

发生此错误是因为 object safety

为了能够从特征创建特征对象,特征必须是对象安全的。如果这两个语句都成立,则特征是对象安全的:

  1. 它没有 Sized 要求,如 trait Whatever: Sized {};
  2. 它的所有方法都是对象安全的。

如果以下两个陈述都为真,则方法是对象安全的:

  1. 它有 where Self: Sized 要求,如 fn method() where Self: Sized;
  2. none以下语句成立:

    1. 此方法在其签名中以任何形式提及 Self,即使在引用下,关联类型除外;
    2. 此方法是静态的;
    3. 此方法是通用的。

如果你多想想,这些限制实际上是很自然的。

请记住,当值被制成特征对象时,它们类型的实际信息将被删除,包括它们的大小。因此,特征对象只能通过引用来使用。引用(或其他智能指针,如 BoxRc),当应用于 trait 对象时,变为 "fat pointers" - 除了指向值的指针,它们还包含指向虚拟对象的指针table 该值。

因为 trait 对象只能通过指针使用,所以不能对它们调用按值 self 方法 - 您需要实际值才能调用此类方法。这在某一点上违反了对象安全性,这意味着不能将具有此类方法的特征作为特征对象,但是,即使在 1.0 之前,规则也已被调整为允许特征对象上的按值 self 方法.但是,由于上述原因,这些方法仍然无法调用。有理由期待这个限制在未来会被取消,因为它目前会导致语言中的一些怪癖,例如,无法调用 Box<FnOnce()> 闭包。

Self 不能用在应该在 trait 对象上调用的方法中,因为 trait 对象的实际类型已被擦除,但为了调用这些方法,编译器需要知道这个被擦除的类型.

为什么不能在特征对象上调用静态方法,我想,这是显而易见的 - 静态方法根据定义 "belong" 特征本身,而不是值,所以你需要知道具体类型实现调用它们的特征。更具体地说,常规方法是通过存储在特征对象中的虚拟 table 进行调度的,但是静态方法没有接收者,因此它们没有任何可调度的东西,因此它们不能存储在虚拟 table。因此,在不知道具体类型的情况下,它们是不可调用的。

我认为由于其他原因不能调用通用特征方法,技术性多于逻辑性。在 Rust 中,泛型函数和方法是通过单态化实现的——也就是说,对于具有一组具体类型参数的泛型函数的每个实例化,编译器都会生成一个单独的函数。对于语言用户来说,他们似乎在调用通用函数;但在最低级别上,每组类型参数都有一个单独的函数副本,专门用于实例化类型。

鉴于这种方法,为了在特征对象上调用泛型方法,您需要它的虚拟 table 包含指向 几乎每个可能的 实例化的指针所有可能类型的通用方法,这自然是不可能的,因为它需要无限数量的实例化。因此,不允许在特征对象上调用泛型方法。

如果 Drawable 是一个外部特征,那么你就会陷入困境 - 不可能做你想做的,也就是说,对异构集合中的每个项目调用 draw()。如果您的可绘制对象集是静态已知的,您可以为每个可绘制对象类型创建一个单独的集合,或者创建您自己的 enum,其中包含您拥有的每个可绘制对象类型的变体。然后您可以为枚举本身实现 Drawable,这将非常简单。

如果您无法接受所提供的内容,可以尝试两种选择。

在这种情况下,你不能,但如果给你一个未定尺寸的 RenderTarget

trait Drawable {
    fn draw<RT: RenderTarget + ?Sized>(&self, target: &mut RT);
}

你可以实施

trait DrawableDynamic {
    fn draw(&self, target: &mut RenderTarget);
}

impl<T: Drawable> DrawableDynamic for T {
    fn draw(&self, target: &mut RenderTarget) {
        Drawable::draw(self, target)
    }
}

将给定的类型重定向到对象安全的动态分派替代方案。看起来可以在上游进行这样的更改,因为您不能真正 使用 RT 大小这一事实。

另一个不允许您在 Vec 中放置任意 Drawable,但应该在不允许上游未确定大小的类型的情况下工作。这是使用枚举来包装向量的可能值:

enum AllDrawable {
    Square(Square),
    Triangle(Triangle)
}

impl Drawable for AllDrawable {
    fn draw<RT: RenderTarget>(&self, target: &mut RT) {
        match *self { 
            AllDrawable::Square(ref x) => x.draw(target),
            AllDrawable::Triangle(ref x) => x.draw(target),
        }
    }
}

有人可能想添加 From 实现等;如果使用 wrapped_enum! 会自动为您实现这些,您可能会发现它更容易。

我参考了 Vladimir 的出色回答,它解释了 Object 的安全性,但是我担心在讨论过程中忘记了手头的具体问题。

正如 Vladimir 所提到的,问题是一个泛型方法(泛型生命周期很好)使得它所属的特征对于 运行 时间多态性不可用;这在 Rust 中称为对象安全。

因此,最简单的修复方法是删除方法的通用参数!

trait RenderTarget {}

struct RenderWindow;
impl RenderTarget for RenderWindow {}

trait Drawable {
    fn draw(&self, target: &mut RenderTarget);
}

fn main() {
    let mut win = RenderWindow;
    let mut vec: Vec<Box<Drawable>> = Vec::new();

    for e in &vec {
        e.draw(&mut win);
    }
}

主要区别:

fn draw<RT: RenderTarget>(&self, target: &mut RT)

fn draw(&self, target: &mut RenderTarget)

后者也需要 RenderTarget 是对象安全的,因为它现在用于 运行 时间多态性情况(因此,没有静态方法,没有泛型方法,没有 Self, ...).

另一个(更技术性的)区别是前者在编译时是 "monorphised" (即 RT 被替换为实际类型并应用了所有相关优化)而后者不是(因此,不会发生此类优化)。