有两种方法可以为Trait本身提供方法,Rustdoc通过说“提供的方法”和impl dyn XXX
来区分它们。例如:
trait Trait {
fn foo(&self) {
println!("Default implementation");
}
}
impl Trait {
fn bar(&self) {
println!("Anonymous implementation?");
}
}
在阅读Rust的failure
箱子的文档时,我注意到了。
它们的用例是什么?有什么区别?
答案 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
很少见。通常,仅当您要使用一些动态类型信息时才有用,例如向下转换为实际类型。标准库中特征对象上具有固有方法的唯一特征是Any
和Error
。
答案 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作为第一个参数的自由函数;但是,不能在方法位置调用自由函数。