我使用《Rust By Practice》一书练习了 Rust 编码。我遇到过一个让我有点困惑的例子。以下代码是Traits章节中第四个练习的修订版:
use std::ops;
#[derive(PartialEq, Debug)]
struct LeftHandSide;
#[derive(PartialEq, Debug)]
struct RightHandSide;
#[derive(PartialEq, Debug)]
struct LeftAndRightHandSide;
impl ops::Add<RightHandSide> for LeftHandSide{
type Output = LeftAndRightHandSide;
fn add(self, _rhs: RightHandSide) -> LeftAndRightHandSide{
LeftAndRightHandSide
}
}
fn main() {
assert_eq!(LeftHandSide + RightHandSide, LeftAndRightHandSide);
println!("Success!");
}
它是通过派生结构的
PartialEq
和
Debug
特征默认实现来完成的。这是强制性的,因为 assert_eq!
宏比较并打印(紧急情况下)表达式的值。此外,Add
特征的左侧和右侧必须处于正确的顺序。添加特征需要 type Output
别名,但它没有在任何地方使用。根据
添加文档,别名也未使用。 为什么在实现
std::ops::Add
特性时需要类型别名?
让我们看一个例子。我们有这个特点:
trait Glonk {
type Wobble: Default;
}
现在我们在通用函数中使用这个特征:
fn get_wobble<T: Glonk>() -> T::Wobble {
Default::default()
}
您可以看到我们需要
T
来实现 Glonk
。由于
Wobble
类型是
Glonk
公共接口的一部分,我们知道
Glonk
的任何实现者都有
T::Wobble
,因此我们可以在我们的函数中使用它。这就是特质的重点。我们不确切知道我们得到的是什么类型,但我们可以保证我们得到的类型实现了特征中声明的所有内容。如果我们想为某个类型实现
Glonk
,我们需要为该特定类型定义
Wobble
。
现在让我们看看
Add
特征的定义。
pub trait Add<Rhs = Self> {
type Output;
fn add(self, rhs: Rhs) -> Self::Output;
}
这比我们的 Glonk
特征稍微复杂一些,但我们可以看到该特征声明了关联的类型 Output
。这意味着该特征的任何实现者都需要包含
Output
类型的定义。该类型是否在任何地方使用并不重要。它是界面的一部分,因此任何具有该特征的消费者都可以
使用它。该类型也is用作
add()
的返回类型,但对于是否需要在Add
的实现中定义它的问题来说并不重要。
在 Rust 的未来版本中,最终将可以声明关联类型默认值
:
trait Glonk {
type Wobble: Default = i32;
}
Add
特征无论如何都不会实现其 Output
类型的默认值,因此这不会改变此特定示例的任何内容。