本文节选自<<Rust语言圣经>>一书
欢迎大家加入Rust编程学院,一起学习交流:
QQ群:1009730433
深入了解特征
特征之于Rust更甚于接口之于其他语言,因此特征在Rust中很重要也相对较为复杂,我们决定把特征分为两篇进行介绍,第一篇在之前已经讲过,现在就是第二篇:关于特征的进阶篇,会讲述一些你不常用到但是该了解的特性。
关联类型
在方法一章中,我们将到了关联函数,但是实际上关联类型和关联函数并没有任何交集,虽然它们的名字有一半的交集。
关联类型是在特征定义的语句块中,申明一个自定义类型,这样就可以在特征的方法签名中使用该类型:
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
}
以上是标准库中的迭代器特征Iterator
,它有一个Item
关联类型,用于替代遍历的值的类型。
同时,next
方法也返回了一个Item
类型,不过使用Option
枚举进行了包裹,假如迭代器中的值是i32
类型,那么调用next
方法就将获取一个Option<i32>
的值。
还记得Self
吧?在之前的章节提到过, Self
用来指代当前的特征实例 ,那么Self::Item
就用来指代特征实例中具体的Item
类型:
impl Iterator for Counter {
type Item = u32;
fn next(&mut self) -> Option<Self::Item> {
// --snip--
在上述代码中,我们为Counter
类型实现了Iterator
特征,那么Self
就是当前的Iterator
特征对象,Item
就是u32
类型。
聪明的读者之所以聪明,因为你们喜欢联想和举一反三,同时你们也喜欢提问:为何不用泛型,例如如下代码
pub trait Iterator<Item> {
fn next(&mut self) -> Option<Item>;
}
答案其实很简单,为了代码的可读性. 当你使用了泛型后,你需要在所有地方都这样写Iterator<Item>
,而使用了关联类型,你只需要这样写Iterator
,当类型定义复杂时,这种写法可以极大的增加可读性:
pub trait CacheableItem: Clone + Default + fmt::Debug + Decodable + Encodable {
type Address: AsRef<[u8]> + Clone + fmt::Debug + Eq + Hash;
fn is_null(&self) -> bool;
}
例如上面的代码,Address
自然远比AsRef<[u8]> + Clone + fmt::Debug + Eq + Hash
的要简单的多,而且含义清晰。
再例如,如果使用泛型,你将得到以下的代码:
trait Container<A,B> {
fn contains(&self,a: A,b: B) -> bool;
}
fn difference<A,B,C>(container: &C) -> i32 where
C : Container<A,B> {
...}
而使用关联类型,将得到可读性好的多的代码:
trait Container{
type A;
type B;
fn contains(&self, a: &Self::A