什么时候使用关联类型和通用类型比较合适?


107

这个问题中,出现了一个问题,可以通过将使用通用类型参数的尝试更改为关联类型来解决。这提示了一个问题“为什么关联类型在这里更合适?”,这使我想知道更多。

引入关联类型RFC表示:

该RFC通过以下方式阐明了特征匹配:

  • 将所有特征类型参数视为输入类型,以及
  • 提供关联的类型,它们是输出类型

RFC使用图结构作为激励示例,并且在文档中也使用了图结构,但是我承认,与类型参数化版本相比,我不完全意识到关联类型版本的好处。最主要的是该distance方法无需关心Edge类型。很好,但是似乎根本没有关联类型的原因。

我发现关联类型在实践中使用起来非常直观,但是当我决定在自己的API中何时何地使用它们时,我发现自己很挣扎。

在编写代码时,何时应在泛型类型参数上选择关联类型,而何时应相反?

Answers:


75

现在,在Rust编程语言的第二版中对此进行了介绍。但是,让我们进一步探讨一下。

让我们从一个简单的例子开始。

什么时候使用特征方法合适?

提供后期绑定有多种方法:

trait MyTrait {
    fn hello_word(&self) -> String;
}

要么:

struct MyTrait<T> {
    t: T,
    hello_world: fn(&T) -> String,
}

impl<T> MyTrait<T> {
    fn new(t: T, hello_world: fn(&T) -> String) -> MyTrait<T>;

    fn hello_world(&self) -> String {
        (self.hello_world)(self.t)
    }
}

忽略任何实施/性能策略,以上两个摘录均允许用户动态指定hello_world行为方式。

一个区别(在语义上)是,该trait实现保证了对于T实现的给定类型traithello_world将始终具有相同的行为,而该struct实现则允许在每个实例的基础上具有不同的行为。

使用方法是否合适取决于用例!

什么时候适合使用关联类型?

与上述trait方法类似,关联类型是后期绑定的一种形式(尽管它在编译时发生),允许的用户trait为给定实例指定要替换的类型。这不是唯一的方法(因此是问题):

trait MyTrait {
    type Return;
    fn hello_world(&self) -> Self::Return;
}

要么:

trait MyTrait<Return> {
    fn hello_world(&Self) -> Return;
}

等效于以上方法的后期绑定:

  • 第一个强制要求对于给定的条件Self,只有一个Return关联
  • 第二个,相反,允许实现MyTrait针对Self多个Return

哪种形式更合适取决于实施统一性是否有意义。例如:

  • Deref 使用关联的类型,因为没有唯一性,编译器会在推理过程中发疯
  • Add 使用关联的类型,因为其作者认为给定两个参数将有一个逻辑返回类型

如您所见,虽然Deref有一个明显的用例(技术约束),但用例的Add含义却不太明确:也许i32 + i32产生一个i32Complex<i32>取决于上下文是有意义的?尽管如此,作者还是行使了自己的判断力,认为没有必要重载返回类型来添加。

我个人的立场是没有正确的答案。仍然,除了unicity参数之外,我还要指出,关联类型使使用trait更加容易,因为它们减少了必须指定的参数数量,因此,如果使用常规trait trait参数的灵活性的好处不明显,建议从关联类型开始。


4
让我尝试简化一下: trait/struct MyTrait/MyStruct允许恰好一个impl MyTrait forimpl MyStructtrait MyTrait<Return>允许多个,impl因为它是通用的。Return可以是任何类型。通用结构是相同的。
Paul-Sebastian Manole

2
我发现您的答案比“ The Rust Programming Language”中的答案更容易理解
drojf

“第一个强制要求,对于给定的自我,只有一个关联的回报”。就目前而言,这是正确的,但是当然可以通过使用泛型特征进行子类化来解决此限制。也许唯一性只能是一个建议,而不能被强制执行
joel

36

关联类型是一种分组机制,因此在类型分组在一起时,应使用它们。

Graph文档中介绍的特征就是一个例子。您希望a Graph是通用的,但是一旦有了特定类型的Graph,您就不希望NodeEdge类型有所变化。一个特定的人Graph不想在一个实现中改变这些类型,实际上,他们希望它们总是相同的。它们被组合在一起,或者甚至可以说是associated


4
我花了一些时间来理解。对我来说,它更像是一次定义几种类型:“边缘”和“节点”在图表中没有意义。
tafia
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.