1.16.1. 让特征泛型的两种方式
特征可以通过两种方式实现泛型:
- 泛型类型参数。例如:
trait Foo<T> - 关联类型。例如:
trait Foo { type Bar; }
二者的区别在于:
- 对于关联类型,特定类型的给定特征只有一种实现
- 对于泛型参数,则可以存在多种实现
简单建议:如果可能,优先使用关联类型。
1.16.2. 泛型(类型参数)特征
泛型特征要求你指定所有泛型类型参数并重复它们的约束。
这在维护上会更困难。例如,如果你向特征添加一个泛型类型参数,该特征的所有实现者都必须更新他们的代码。
这种形式还可能导致一个特征对给定类型存在多个实现的问题。此时编译器更难推断出你实际想要的特征实例。有时你必须调用消除歧义的函数,例如FromIterator::<u32>::from_iter。
在某些情况下,此特性也是一个优势,例如:
-
impl PartialEq<BookFormat> for Book,其中BookFormat可以是不同类型 - 你可以同时实现
FromIterator<T>和FromIterator<&T> where T: Clone
1.16.3. 关联类型特征
我们用一段代码作为示例:
trait Contains {
type A;
type B;
// 更新语法以泛型方式引用这些新类型
fn contains(&self, _: &Self::A, _: &Self::B) -> bool;
}
进入全屏模式 退出全屏模式
使用关联类型时:
- 编译器只需要知道实现该特征的类型
- 约束可以完全放在特征本身上,无需重复
- 将来添加另一个关联类型不会影响用户
- 具体类型决定了特征内的关联类型,因此无需使用消除歧义的函数。看这个示例:
impl Contains for Container {
// 指定`A`和`B`的类型。如果`input`类型
// 是`Container(i32, i32)`,则`output`类型被确定
// 为`i32`和`i32`。
type A = i32;
type B = i32;
// 这里`&Self::A`和`&Self::B`同样有效。
fn contains(&self, number_1: &i32, number_2: &i32) -> bool {
(&self.0 == number_1) && (&self.1 == number_2)
}
// 获取第一个数字。
fn first(&self) -> i32 { self.0 }
// 获取最后一个数字。
fn last(&self) -> i32 { self.1 }
}
进入全屏模式 退出全屏模式
- 此示例为
Container类型实现了Contains特征 Container的Contains实现中的关联类型由type A = i32;和type B = i32;确定
不能为多个目标类型实现 Deref
查看Deref特征的源代码:
pub trait Deref {
type Target: ?Sized;
fn deref(&self) -> &Self::Target;
}
进入全屏模式 退出全屏模式
-
type Target: ?Sized;中的Target是我们所说的目标类型
我们编写一个Deref实现来说明:
use std::ops::Deref;
struct Wrapper {
value: String,
}
impl Deref for Wrapper {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.value
}
}
进入全屏模式 退出全屏模式
在上面的代码中,Wrapper只能被解引用为String。但如果你想让Wrapper同时被解引用为String和str,Rust不允许你再次实现Deref,因为Target只能有一个具体类型。
换句话说,这是非法的:
use std::ops::Deref;
struct Wrapper {
value: String,
}
// 第一个Deref实现,Target = String
impl Deref for Wrapper {
type Target = String;
fn deref(&self) -> &Self::Target {
&self.value
}
}
// 这是非法的:Rust不允许为同一个`Wrapper`类型实现第二个`Deref`
impl Deref for Wrapper {
type Target = str; // 冲突:Rust无法推断哪个`Target`适用
fn deref(&self) -> &Self::Target {
&self.value
}
}
进入全屏模式 退出全屏模式
不能使用多个 Item 来实现 Iterator 特征
原因与不能为多个目标类型实现Deref的原因相同:主要涉及关联类型的唯一性和 Rust 编译器的推断规则。
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.