Types 何时使用关联类型与泛型类型比较合适?

Types 何时使用关联类型与泛型类型比较合适?,types,rust,idioms,Types,Rust,Idioms,在中,出现了一个问题,可以通过将使用泛型类型参数的尝试更改为关联类型来解决。这引发了一个问题:“为什么关联类型在这里更合适?”,这让我想知道更多 报告说: 本RFC通过以下方式澄清特征匹配: 将所有特征类型参数视为输入类型,以及 提供关联类型,即输出类型 RFC使用一个图形结构作为一个激励示例,这也在中使用,但我承认并没有完全理解关联类型版本相对于类型参数化版本的好处。最主要的是distance方法不需要关心Edge类型。这很好,但似乎有点肤浅的理由有关联的类型 我发现关联类型在实践中使用起

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

报告说:

本RFC通过以下方式澄清特征匹配:

  • 将所有特征类型参数视为输入类型,以及
  • 提供关联类型,即输出类型
RFC使用一个图形结构作为一个激励示例,这也在中使用,但我承认并没有完全理解关联类型版本相对于类型参数化版本的好处。最主要的是
distance
方法不需要关心
Edge
类型。这很好,但似乎有点肤浅的理由有关联的类型

我发现关联类型在实践中使用起来非常直观,但我发现自己在决定在自己的API中何时何地使用它们时遇到了困难

在编写代码时,什么时候应该选择关联类型而不是泛型类型参数,什么时候应该选择相反的操作?

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

文档中介绍的
图形
特性就是一个例子。您希望
图形
是泛型的,但一旦有了特定类型的
图形
,就不希望
节点
类型再发生变化。一个特定的
不希望在单个实现中改变这些类型,事实上,它希望它们总是相同的。它们被分组在一起,甚至可以说是关联的。

这一点现在在中进行了讨论。不过,让我们再深入一点

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

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

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

或:

或:

trait-MyTrait{
fn hello_world(&Self)->返回;
}
相当于上述方法的后期绑定:


  • 第一种方法强制规定,对于给定的
    Self
    有一个
    返回
    关联
  • 相反,第二种方法允许为
    Self
    实现
    MyTrait
    ,为多个
    Return
哪种形式更合适取决于强制实施单一性是否有意义。例如:

  • Deref
    使用关联类型,因为如果没有unicity,编译器将在推理过程中发疯
  • Add
    使用关联类型,因为其作者认为给定这两个参数将有一个逻辑返回类型
正如您所看到的,虽然
Deref
是一个明显的用例(技术约束),但是
Add
的情况就不那么明确了:根据上下文,
i32+i32
可能会产生
i32
复杂的
?尽管如此,作者还是运用了他们的判断,认为没有必要为添加内容重载返回类型


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

关联类型可以用来告诉编译器“这两个实现之间的这两种类型是相同的”。下面是一个编译的双分派示例,与标准库将迭代器与求和类型关联的方式几乎相似:

trait MySum {
    type Item;
    fn sum<I>(iter: I)
    where
        I: MyIter<Item = Self::Item>;
}

trait MyIter {
    type Item;
    fn next(&self) {}
    fn sum<S>(self)
    where
        S: MySum<Item = Self::Item>;
}

struct MyU32;

impl MySum for MyU32 {
    type Item = MyU32;

    fn sum<I>(iter: I)
    where
        I: MyIter<Item = Self::Item>,
    {
        iter.next()
    }
}

struct MyVec;

impl MyIter for MyVec {
    type Item = MyU32;
    fn sum<S>(self)
    where
        S: MySum<Item = Self::Item>,
    {
        S::sum::<Self>(self)
    }
}

fn main() {}

trait MySum{
类型项目;
fn总和(国际热核实验堆:一)
哪里
I:密特;
}
特征密特{
类型项目;
fn下一个(&self){}
fn总和(自身)
哪里
S:MySum;
}
结构MyU32;
MyU32的impl MySum{
项目类型=MyU32;
fn总和(国际热核实验堆:一)
哪里
I:密特,
{
iter.next()
}
}
结构MyVec;
MyVec的impl-MyIter{
项目类型=MyU32;
fn总和(自身)
哪里
S:MySum,
{
S::sum::(self)
}
}
fn main(){}
此外,我们也有一些关于这方面的好信息:

简而言之,当您希望键入
A
以能够对不同类型参数实现任意次数的trait时,可以使用泛型,例如在From trait的情况下

如果一个类型只实现一次trait是有意义的,那么可以使用关联的类型,例如使用Iterator和Deref


我花了一些时间才明白。在我看来,它更像是一次定义几个类型:边和节点在图形中没有意义。让我试着简化一点:
trait/struct MyTrait/MyStruct
只允许一个
impl MyTrait for
impl MyStruct
trait MyTrait
允许多个
impl
s,因为它是泛型的<代码>返回可以是任何类型。泛型结构是一样的。我发现你的答案比“Rust编程语言”中的答案更容易理解。第一个答案强调,对于给定的自我,有一个单一的返回关联。这在直接意义上是正确的,但是我们当然可以通过使用泛型特征进行子类化来绕过这个限制。也许单一性只能是一种暗示,而不是强制执行
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)
    }
}
trait MyTrait {
    type Return;
    fn hello_world(&self) -> Self::Return;
}
trait MyTrait<Return> {
    fn hello_world(&Self) -> Return;
}
trait MySum {
    type Item;
    fn sum<I>(iter: I)
    where
        I: MyIter<Item = Self::Item>;
}

trait MyIter {
    type Item;
    fn next(&self) {}
    fn sum<S>(self)
    where
        S: MySum<Item = Self::Item>;
}

struct MyU32;

impl MySum for MyU32 {
    type Item = MyU32;

    fn sum<I>(iter: I)
    where
        I: MyIter<Item = Self::Item>,
    {
        iter.next()
    }
}

struct MyVec;

impl MyIter for MyVec {
    type Item = MyU32;
    fn sum<S>(self)
    where
        S: MySum<Item = Self::Item>,
    {
        S::sum::<Self>(self)
    }
}

fn main() {}