有两种方法可以为Trait本身提供方法,Rustdoc通过说“提供的方法”和来区分它们impl dyn XXX。例如:
trait Trait {
fn foo(&self) {
println!("Default implementation");
}
}
impl Trait {
fn bar(&self) {
println!("Anonymous implementation?");
}
}
Run Code Online (Sandbox Code Playgroud)
当我阅读Rust的failure箱子的文档时,我注意到了。
他们有哪些用例?有什么区别?
第一个片段,
trait Trait {
fn foo(&self) {
println!("Default implementation");
}
}
Run Code Online (Sandbox Code Playgroud)
在特征上实现提供的方法。trait实现可以覆盖此方法,但是不必覆盖它。
第二个片段
impl Trait {
fn bar(&self) {
println!("Anonymous implementation?");
}
}
Run Code Online (Sandbox Code Playgroud)
在类型为的特征对象上实现固有方法。只能针对特征对象(例如type)调用for的方法实现。它们不能按值接收,因为在编译时没有已知的大小,也不能在实现的具体类型(包括有界的泛型)上调用它们。dyn Traitdyn Trait&dyn Traitselfdyn TraitTraitTrait
现代的表示法是impl dyn Trait代替impl Trait,实际上,这种表示法是引入dyn关键字的激励示例之一 –旧的语法没有提供有关语义含义的任何线索,而新的语法带有dyn关键字提示实际上,此impl仅与动态调度一起使用。
特征对象是指向实现的胖指针Trait,但是对象的具体类型在编译时不一定是已知的。胖指针包含指向对象数据的指针,以及指向对象类型的虚拟方法表的指针。后者用于在运行时动态调度到正确的特征实现。
使用是相当不常见的impl dyn Trait。通常,仅当您要使用一些动态类型信息(如向下转换为实际类型)时,此选项才有用。标准库中特征对象上具有固有方法的唯一特征是Any和Error。
简而言之:一个可以被覆盖,另一个则不能。
当您定义特征时,您定义了该特征的实现可能(或必须)覆盖的项目:
trait Trait {
fn foo(&self) {
println!("Default implementation");
}
}
impl Trait for i64 {
fn foo(&self) {
println!("i64 implementation: {}", self);
}
}
Run Code Online (Sandbox Code Playgroud)
另一方面,使用impl Trait,您可以定义不能被重写的固有方法:
impl Trait {
fn bar(&self) {
self.foo();
self.foo()
}
}
// Try:
impl Trait for i64 {
fn bar(&self) { ... } // error: bar cannot be overridden.
}
Run Code Online (Sandbox Code Playgroud)
因此,固有特征方法充当模板方法模式:它们提供了一个将一个或多个可重写方法链接在一起的画布。
如果您查看failure链接的板条箱,该方法Failure::find_root_cause()会指出:
这相当于迭代
iter_causes()并获取最后一项。
您可能会认为这些固有方法是便利方法,这些方法为可以手动完成的常见任务提供简单/直观的界面......但可以方便地预先定义。
注意:任何固有方法都可以作为自由函数实现,并将特征作为第一个参数;然而,自由函数不能在方法位置调用。