现在,在Rust编程语言的第二版中对此进行了介绍。但是,让我们进一步探讨一下。
让我们从一个简单的例子开始。
什么时候使用特征方法合适?
提供后期绑定有多种方法:
trait MyTrait {
fn hello_word(&self) -> String;
}
要么:
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;
}
要么:
trait MyTrait<Return> {
fn hello_world(&Self) -> Return;
}
等效于以上方法的后期绑定:
- 第一个强制要求对于给定的条件
Self
,只有一个Return
关联
- 第二个,相反,允许实现
MyTrait
针对Self
多个Return
哪种形式更合适取决于实施统一性是否有意义。例如:
Deref
使用关联的类型,因为没有唯一性,编译器会在推理过程中发疯
Add
使用关联的类型,因为其作者认为给定两个参数将有一个逻辑返回类型
如您所见,虽然Deref
有一个明显的用例(技术约束),但用例的Add
含义却不太明确:也许i32 + i32
产生一个i32
或Complex<i32>
取决于上下文是有意义的?尽管如此,作者还是行使了自己的判断力,认为没有必要重载返回类型来添加。
我个人的立场是没有正确的答案。仍然,除了unicity参数之外,我还要指出,关联类型使使用trait更加容易,因为它们减少了必须指定的参数数量,因此,如果使用常规trait trait参数的灵活性的好处不明显,建议从关联类型开始。
trait/struct MyTrait/MyStruct
允许恰好一个impl MyTrait for
或impl MyStruct
。trait MyTrait<Return>
允许多个,impl
因为它是通用的。Return
可以是任何类型。通用结构是相同的。