什么时候使用关联types与genericstypes是否合适?
在这个问题中 ,出现了一个问题,可以通过改变尝试将genericstypes参数用于关联types来解决。 这促使了 “为什么这里的关联types更合适? ,这让我想知道更多。
引入关联types的RFC说:
本RFC通过以下方式阐明特征匹配:
- 将所有特征types参数作为inputtypes进行处理
- 提供关联types,即输出types 。
RFC使用graphics结构作为激励的例子,这也在文档中使用 ,但是我承认并不完全理解关联types版本相对于types参数化版本的好处。 首要的是distance
方法不需要关心Edge
types。 这是很好的,但似乎有点关联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
的给定typesT
, hello_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
根据上下文产生i32
或Complex<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的多个函数来约束参数。