喜欢的话别忘了点赞、收藏加关注哦(加关注即可阅读全文),对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)
这篇文章以概念性的和建议性的文字偏多,需要你对泛型类型参数和关联类型有一定了解,如果没有了解的请先看【Rust自学】19.2.1. 在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
我们以一段代码为例:
rust">trait Contains {type A;type B;//Updates syntax to refer to these new types genericallyfn contains(&self, _: &Self::A, _: &Self::B) -> bool;
}
使用关联类型:
- 编译器只需要知道实现trait的类型
- 约束(bound) 可以完全位于trait本身,不必重复使用
- 未来再添加关联类型也不影响用户使用
- 具体的类型会决定trait内关联类型的类型,无需使用消除歧义的函数,看个例子:
rust">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
类型实现Contains
trait Container
类型的Contains
trait中的关联类型通过type A = i32;
和type B = i32;
这两行代码决定
不可以对多个目标(Target)类型来实现Deref
trait
看一下Deref
trait的源代码:
rust">pub trait Deref {type Target: ?Sized;fn deref(&self) -> &self::Target;
}
type Target: ?Sized;
中的Target
就是我们说的目标(Target)类型
我们随便写一个Deref
trait的实现来说明一下:
rust">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
只能有一个具体类型。
也就是说,这么写是非法的:
rust">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
trait
其原因与不可以对多个目标(Target)类型来实现Deref
trait一样,主要涉及关联类型的唯一性和Rust 编译器的推断规则。