现在涉及到这一点第二版Rust 编程语言 https://doc.rust-lang.org/book/ch19-03-advanced-traits.html#specifying-placeholder-types-in-trait-definitions-with-associated-types。不过,让我们进一步深入了解一下。
让我们从一个更简单的例子开始。
什么时候适合使用特征方法?
有多种方式提供后期绑定:
trait MyTrait {
fn hello_word(&self) -> String;
}
Or:
struct MyTrait<T> {
t: T,
hello_world: fn(&T) -> String,
}
impl<T> MyTrait<T> {
fn new(t: T, hello_world: fn(&T) -> String) -> MyTrait<T>;
fn hello_world(&self) -> String {
(self.hello_world)(self.t)
}
}
无论任何实现/性能策略,上面的两个摘录都允许用户以动态方式指定如何hello_world
应该表现。
唯一的区别(语义上)是trait
实现保证对于给定类型T
实施trait
, hello_world
将始终具有相同的行为,而struct
实现允许在每个实例的基础上有不同的行为。
使用方法是否合适取决于用例!
什么时候适合使用关联类型?
类似地trait
在上面的方法中,关联类型是后期绑定的一种形式(尽管它发生在编译时),允许用户trait
指定给定实例要替换的类型。这不是唯一的方法(因此是问题):
trait MyTrait {
type Return;
fn hello_world(&self) -> Self::Return;
}
Or:
trait MyTrait<Return> {
fn hello_world(&Self) -> Return;
}
相当于上面方法的后期绑定:
- 第一个强制执行给定的
Self
有一个Return
联系
- 相反,第二个允许实施
MyTrait
for Self
对于多个Return
哪种形式更合适取决于强制唯一性是否有意义。例如:
-
Deref
使用关联类型,因为如果没有唯一性,编译器在推理过程中会发疯
-
Add
使用关联类型是因为它的作者认为给定两个参数就会有一个逻辑返回类型
正如你所看到的,同时Deref
是一个明显的用例(技术限制),Add
不太明确:也许这对i32 + i32
产生任一i32
or Complex<i32>
取决于上下文?尽管如此,作者还是运用了自己的判断,认为没有必要重载添加的返回类型。
我个人的立场是没有正确答案。尽管如此,除了唯一性参数之外,我还要提到关联类型使使用特征变得更容易,因为它们减少了必须指定的参数数量,因此,如果使用常规特征参数的灵活性的好处并不明显,我建议从相关类型开始。