何时使用关联类型与泛型类型是否合适?

She*_*ter 84 types idiomatic rust

这个问题中,出现了一个问题,可以通过改变将泛型类型参数用于关联类型的尝试来解决.这引发了一个问题"为什么相关类型在这里更合适?",这让我想知道更多.

引入相关类型RFC说:

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

  • 将所有特征类型参数视为输入类型,和
  • 提供相关类型,即输出类型.

RFC使用图形结构作为激励示例,这也在文档中使用,但我承认不完全理解相关类型版本相对于类型参数化版本的好处.主要的是该distance方法不需要关心Edge类型.这很好,但似乎有一点点关联类型的原因.

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

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

Mat*_* M. 55

现在在第二版The Rust Programming Language中对此进行了描述.但是,让我们再深入了解一下.

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

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

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

trait MyTrait {
    fn hello_word(&self) -> String;
}
Run Code Online (Sandbox Code Playgroud)

要么:

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

无论任何实现/性能策略,上述两个摘录都允许用户以动态方式指定hello_world应该如何操作.

唯一的区别(语义)就是trait实现保证给定类型的T实施trait,hello_world将始终具有相同的行为而struct实施允许其在每个实例的基础不同的行为.

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

何时适合使用相关类型?

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

trait MyTrait {
    type Return;
    fn hello_world(&self) -> Self::Return;
}
Run Code Online (Sandbox Code Playgroud)

要么:

trait MyTrait<Return> {
    fn hello_world(&Self) -> Return;
}
Run Code Online (Sandbox Code Playgroud)

相当于上面方法的后期绑定:

  • 第一个强制执行,给定Self一个Return关联
  • 第二个,相反,允许实现MyTrait针对Self多个Return

哪种形式更合适取决于是否有必要强制执行单一性.例如:

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

正如你所看到的,虽然Deref是一个明显的用例(技术约束),但案例Add不那么明确:或许它可以i32 + i32产生任何一个i32Complex<i32>取决于上下文?尽管如此,提交人仍然行使其判断,并决定重新加载返回类型以进行添加是不必要的.

我的个人立场是没有正确的答案.但是,除了unicity论证之外,我还会提到相关类型使得使用特征更容易,因为它们减少了必须指定的参数数量,所以如果使用常规特征参数的灵活性的好处不明显,我建议从相关类型开始.

  • 我发现您的答案比“Rust 编程语言”中的答案更容易理解 (4认同)
  • 让我尝试简化一下:`trait / struct MyTrait / MyStruct`恰好允许一个`impl MyTrait for`或`impl MyStruct`。'trait MyTrait &lt;Return&gt;`允许多个`impl`,因为它是通用的。`Return`可以是任何类型。通用结构是相同的。 (2认同)

Ste*_*nik 29

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

Graph文档中介绍的特性就是一个例子.你想要一个Graph通用的,但是一旦你有一个特定的类型Graph,你不希望这些NodeEdge类型再变化.特定的Graph是不希望在单个实现中改变这些类型,并且事实上,希望它们始终是相同的.它们被组合在一起,或者甚至可以说是相关联的.

  • 我花了一些时间才明白.对我来说,它看起来更像是同时定义几种类型:Edge和Node在图形中没有意义. (4认同)