特征中两种默认实现样式之间的区别?

时间:2019-01-25 19:47:31

标签: rust

有两种方法可以为Trait本身提供方法,Rustdoc通过说“提供的方法”和impl dyn XXX来区分它们。例如:

trait Trait {
    fn foo(&self) {
        println!("Default implementation");
    }
}

impl Trait {
    fn bar(&self) {
        println!("Anonymous implementation?");
    }
}

在阅读Rust的failure箱子的文档时,我注意到了。

它们的用例是什么?有什么区别?

2 个答案:

答案 0 :(得分:3)

第一个代码段

trait Trait {
    fn foo(&self) {
        println!("Default implementation");
    }
}

在特征上实施提供的方法。特质实现可以重写此方法,但不必重写。

第二个片段

impl Trait {
    fn bar(&self) {
        println!("Anonymous implementation?");
    }
}

对类型为dyn Trait特征对象实施固有方法dyn Trait的方法实现只能用于特征对象,例如&dyn Trait类型的。由于self的大小在编译时未知,因此它们无法按值接收dyn Trait,并且不能在实现的具体类型上调用它们Trait (包括绑定了Trait的通用类型)。

现代符号是写impl dyn Trait而不是impl Trait,实际上,这个符号是one of the motivating examples for the introduction of the dyn keyword –旧语法没有提供任何有关语义的线索,而带有dyn关键字的新语法表明,该impl仅与动态调度一起使用。

特征对象是指向实现Trait的对象的 fat指针,但是对象的具体类型在编译时不一定是已知的。胖指针包含指向对象数据的指针,以及指向对象类型的虚拟方法表的指针。后者用于在运行时动态调度到正确的特征实现。

使用impl dyn Trait很少见。通常,仅当您要使用一些动态类型信息时才有用,例如向下转换为实际类型。标准库中特征对象上具有固有方法的唯一特征是AnyError

答案 1 :(得分:2)

简而言之:一个可以被覆盖,另一个不能被覆盖。

定义特征时,定义的特征可以(或必须)覆盖这些特征的实现:

trait Trait {
    fn foo(&self) {
        println!("Default implementation");
    }
}

impl Trait for i64 {
    fn foo(&self) {
        println!("i64 implementation: {}", self);
    }
}

另一方面,使用impl Trait定义固有的方法,这些方法不能被覆盖:

impl Trait {
    fn bar(&self) {
        self.foo();
        self.foo()
    }
}

// Try:
impl Trait for i64 {
    fn bar(&self) { ... } // error: bar cannot be overridden.
}

结果,固有特征方法充当Template Method Pattern:它们提供了将一个或多个可覆盖方法链接在一起的画布。

如果您查看链接的failure条板箱,则方法Failure::find_root_cause()指出:

  

这等效于遍历iter_causes()并获取最后一项。

您可能会认为这些固有方法是便捷方法,这些方法为常见任务提供了简单/直观的界面, 可以手动完成...但是方便地预先-定义。

注意:任何固有方法都可以实现为以trait作为第一个参数的自由函数;但是,不能在方法位置调用自由函数。