问题描述
我刚刚阅读了 Rust by Example 中的 this article about generics and Traits,他们似乎在通用调用方上实现了 Trait。查看他们的文章以获取完整示例,但这里有一个最小的工作示例:
struct Example;
trait Foo {
fn print_foo(&self);
}
impl<T> Foo for T {
fn print_foo(&self) {
println!("foo");
}
}
fn main() {
let example = Example;
example.print_foo();
}
此示例有效并打印 foo
。这是如何运作的?对于上下文,我阅读了完整的 Rust 书,直到前面提到的文章才看到提到这一点。像这样的通用实现究竟是如何工作的?编译器如何知道将 print_foo
与 Example
相关联?这个 impl
的范围如何?我可以使用 crate 范围的通用实现吗?
解决方法
像这样的通用实现究竟是如何工作的?
您可以或多或少地将泛型视为惰性代码生成:impl<T> Foo for T
的意思是“对于 每一个具体类型 T
,如果 {{1 }} 对于 Foo
是需要的,使用这个函数创建一个。”
编译器如何知道将 T
与 print_foo
相关联?
当编译器看到一个方法调用时,这里Example
,
- 它首先寻找匹配的固有实现,
.print_foo()
。在这种情况下它没有找到。 - 它查看在当前范围内可见的所有特征(已定义或
impl Foo { fn print_foo(&self) {...} }
d),以查看它们中是否有任何一个定义了一个名为use
的方法。立> - 它检查
print_foo
是否实现了这些特征中的任何一个。Example
实现了Example
,因为一切(除了未调整大小的类型)都实现了Foo
,因此使用了该 trait。
Rust 参考的 Method-call expressions 部分描述了这些规则。
这个 Foo
的范围如何?我可以使用 crate 范围的通用实现吗?
特性实现没有范围。它们有效地存在于任何地方,无论可能将什么带入当前范围。 (trait implementation coherence 规则旨在确保找到哪些实现永远不会取决于当前 crate 的依赖项中的哪些 crate,即编译器编译了哪些代码。)
然而,traits 是有作用域的:除非用 impl
或通过在同一个模块中定义它来将 trait 引入作用域,否则永远不会找到 trait 方法。
在您的特定情况下,use
意味着不可能为该特征编写任何其他实现¹ — 没有“板条箱范围的通用实现”这样的东西.
¹ impl<T> Foo for T
并不是“所有类型”的意思。它的意思是“每个大小类型”,因为大多数通用代码无法处理动态大小/“未大小”的类型,例如 <T>
和 dyn Foo
,因此这是一个有用的默认限制.如果您改写 [i32]
,那么泛型确实涵盖了所有可能的类型。