什么时候使用关联types与genericstypes是否合适?

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

引入关联types的RFC说:

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

  • 将所有特征types参数作为inputtypes进行处理
  • 提供关联types,即输出types

RFC使用graphics结构作为激励的例子,这也在文档中使用 ,但是我承认并不完全理解关联types版本相对于types参数化版本的好处。 首要的是distance方法不需要关心Edgetypes。 这是很好的,但似乎有点关联types的原因有点浅。

我发现关联types在实践中非常直观,但是我发现自己在决定何时何地应该在自己的API中使用它们时感到困难。

在编写代码的时候,我应该什么时候select一个genericstypes参数的关联types,什么时候应该做相反的事情?

现在在第二版“锈蚀编程语言”中对此进行了描述。 不过,让我们再深入一点…

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

何时适合使用特质方法?

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

 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) } } 

忽略任何实现/性能策略,上面的摘录都允许用户以dynamic方式指定hello_world应该如何performance。

一个区别在于, trait实现保证对于实现trait的给定typesThello_world将始终具有相同的行为,而struct实现允许在每个实例的基础上具有不同的行为。

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

什么时候适合使用关联的types?

类似于上面的trait方法,关联types是后期绑定的forms(尽pipe它发生在编译时),允许特征的用户为给定实例指定要replace的types。 这不是唯一的方法(因此这个问题):

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

要么:

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

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

  • 第一个强制执行一个给定的Self有一个单一的Return相关
  • 第二个,而是允许实现MyTrait Self多个Return

哪一种forms更为合适取决于强制执行是否合理。 例如:

  • Deref使用关联types,因为没有Deref ,编译器会在推理期间发疯
  • Add使用一个关联的types,因为它的作者认为给定的两个参数将有一个逻辑返回types

正如你所看到的,虽然Deref是一个明显的用例(技术约束),但Add的情况不太清楚:或许i32 + i32根据上下文产生i32Complex<i32>是否i32 ? 然而,提交人行使了自己的判断力,并认定超额返还types是不必要的。

我个人的立场是没有正确的答案。 尽pipe如此,除了唯一性的论点之外,我还要提到关联types使得使用特征变得更容易,因为它们减less了必须指定的参数的数量,所以如果使用规则特征参数的灵活性的好处不明显,build议从相关types开始。

关联的types是一个分组机制,所以应该在将types组合在一起时使用它们。

Graph示例显示了一个示例:您希望Graph是generics的,但是一旦您拥有特定types的Graph,就不希望Node或Edgetypes再变化。 一个特定的Graph并不想在单个实现中改变这些types,事实上,希望它们始终保持一致。 他们被分组在一起,或者甚至可以说…相关联。

至less,这是我的经验法则。

我来自Swift / iOS世界,但我认为这些概念适用:

  • 对于单个函数,您可以使用generics来约束函数的多个参数。

  • 对于一个types,你可以使用一个关联的types,所以你可以通过该types的多个函数来约束参数。