1.16 泛型trait:泛型(类型参数)trait、关联类型trait
这篇文章以概念性和建议性的文字偏多,需要你对泛型类型参数和关联类型有一定了解。
1.16.1. trait的泛型方式
trait的泛型方式有两种:
- 泛型类型参数。例:trait Foo<T>
- 关联类型。例:trait Foo{type Bar;}
两者的区别在于:
- 使用关联类型这种形式的效果就是对于指定类型的trait只有一个实现
- 使用泛型参数类型则可以有多个实现
这里有一个简单的建议:可以的话尽量使用关联类型。
1.16.2. 泛型(类型参数)trait
泛型trait要求必须指定所有的泛型类型参数,并重复写这些参数的约束(bounds)。
这么写维护起来会难一些。比如说如果添加泛型类型参数到某个trait,该trait的所有实现者都必须更新代码。
这种形式还可能会导致针对给定的类型,一个trait有多重实现的问题。编译器会更难推断你想要的到底是trait的哪个实例。有时候不得不调用类似FromIterator::<u32>::from_iter这样可以消除歧义的函数。
这个特性有的时候也会是一个优点,例如:
-impl PatialEq<BookFormat> for Book,其中BookFormat就可以是不同的类型
- 可以同时实现FromIterator<T>和FromIterator<&T> where T:Clone
1.16.3. 关联类型trait
我们以一段代码为例:
trait Contains { type A; type B; //Updates syntax to refer to these new types generically fn contains(&self, _: &Self::A, _: &Self::B) -> bool; }使用关联类型:
- 编译器只需要知道实现trait的类型
-约束(bound)可以完全位于trait本身,不必重复使用
- 未来再添加关联类型也不影响用户使用
- 具体的类型会决定trait内关联类型的类型,无需使用消除歧义的函数,看个例子:
impl Contains for Container { // Specify what types `A` and `B` are. If the `input` type // is `Container(i32, i32)`, the `output` types are determined // as `i32` and `i32`. type A = i32; type B = i32; // `&Self::A` and `&Self::B` are also valid here. fn contains(&self, number_1: &i32, number_2: &i32) -> bool { (&self.0 == number_1) && (&self.1 == number_2) } // Grab the first number. fn first(&self) -> i32 { self.0 } // Grab the last number. fn last(&self) -> i32 { self.1 } }- 这个例子写了为
Container类型实现Containstrait Container类型的Containstrait中的关联类型通过type A = i32;和type B = i32;这两行代码决定
不可以对多个目标(Target)类型来实现Dereftrait
看一下Dereftrait的源代码:
pub trait Deref { type Target: ?Sized; fn deref(&self) -> &Self::Target; }type Target: ?Sized;中的Target就是我们说的目标(Target)类型
我们随便写一个Dereftrait的实现来说明一下:
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来实现Iteratortrait
其原因与不可以对多个目标(Target)类型来实现Dereftrait一样,主要涉及关联类型的唯一性和Rust 编译器的推断规则。