RUST 每日一省:关联类型

        Trait中不仅可以包含方法、常量,还可以包含关联类型。关联类型(associated type)是trait中的类型占位符,它可以被用于trait的方法签名中。trait的实现者需要根据特定的场景来为关联类型指定具体的类型。通过这一技术,我们可以定义出包含某些类型的trait,而无须在实现前确定它们的具体类型是什么。 

Rust标准库中的Iterator就是一个带有关联类型的trait示例.

pub trait Iterator {
    type Item;
    fn next(&mut self) -> Option<Self::Item>;
    ...
} 

        type Item是一个关联类型,使用该类型来替代迭代中出现的值类型。所有实现 Iterator 的类型都必须指定自己的item的类型。

关联类型和泛型参数的区别

        关联类型其实就是trait的“泛型参数”。 只有指定了所有的泛型参数和关联类型, 这个trait才能真正地具体化。 
        泛型参数,以标准库中的AsRef为例。我们希望String类型能实现这个trait,而且既能实现String::as_ref::<str>() 也能实现String::as_ref::<[u8]>()。换句话说,当trait拥有泛型参数时,我们可以为一个类型同时多次实现trait,并在每次实现中改变具体的泛型参数。那么当我们在类型上使用as_ref方法时,也必须提供类型标注来指明想要使用的AsRef实现。 
 
        关联类型,我们不需要在使用该trait的方法时标注类型,因为我们不能为单个类型多次实现这样的trait。 看标准库中的Deref trait。我们希望一个类型实现Deref的时候,最多只能impl一次,解引用的目标类型是唯一固定的,不要让用户在调用obj.deref()方法的时候指定返回类型。因此Deref的目标类型应该设计为“关联类型”。否则,我们可以为一个类型实现多次Deref。解引用的目标类型应该由Self类型唯一确定,不应该在被调用的时候被其他类型干扰。

关联类型的优点


        其优点在于,在实际的编程中,它们允许用户一次性声明关联类型,并在任何特征方法或函数中使用关联类型作为返回类型或参数类型。这消除了类型的冗余声明,与泛型特征的情况类似。 

        例如设计一个泛型的“图”类型,它包含“顶点”和“边”两个泛型参数,如果我们把它们作为普通的泛型参数设计,如下: 

trait Graph<N, E> {
    fn has_edge(&self, &N, &N) -> bool;
    ...
} 

fn distance<N, E, G: Graph<N, E>>(graph: &G, start: &N, end: &N) -> uint {
    ...
} 

        可以看到,泛型参数比较多,也比较麻烦。任何需要一个 Graph 作为参数的函数都需要泛型化的 N和 E类型 ,例如距离计算并不需要 E类型,还是需要声明。

        对于指定的Graph类型,它的顶点和边的类型应该是固定的。在函数签名中再写一遍其实没什么道理。如果我们把普通的泛型参数改为“关联类型”设计,那么数据结构就成了: 

trait Graph {
    type N;
    type E;
    fn has_edge(&self, &N, &N) -> bool;
    ...
} 

fn distance<G>(graph: &G, start: &G::N, end: &G::N) -> uint
    where G: Graph
{
    ...
} 

猜你喜欢

转载自blog.csdn.net/xq723310/article/details/130683155