什么时候使用关联类型与泛型类型合适? [英] When is it appropriate to use an associated type versus a generic type?

查看:35
本文介绍了什么时候使用关联类型与泛型类型合适?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

In this question, an issue arose that could be solved by changing an attempt at using a generic type parameter into an associated type. That prompted the question "Why is an associated type more appropriate here?", which made me want to know more.

引入关联类型的 RFC 说:

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

This RFC clarifies trait matching by:

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

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

The RFC uses a graph structure as a motivating example, and this is also used in the documentation, but I'll admit to not fully appreciating the benefits of the the associated type version over the type-parameterized version. The primary thing is that the distance method doesn't need to care about the Edge type. This is nice, but seems a bit shallow of a reason for having associated types at all.

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

I've found associated types to be pretty intuitive to use in practice, but I find myself struggling when deciding where and when I should use them in my own API.

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

When writing code, when should I choose an associated type over a generic type parameter, and when should I do the opposite?

推荐答案

这在 第二版Rust 编程语言.不过,让我们再深入了解一下.

This is now touched on in the second edition of The Rust Programming Language. However, let's dive in a bit in addition.

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

Let us start with a simpler example.

什么时候使用 trait 方法比较合适?

When is it appropriate to use a trait method?

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

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 应该如何表现.

Disregarding any implementation/performance strategy, both excerpts above allow the user to specify in a dynamic manner how hello_world should behave.

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

The one difference (semantically) is that the trait implementation guarantees that for a given type T implementing the trait, hello_world will always have the same behavior whereas the struct implementation allows having a different behavior on a per instance basis.

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

Whether using a method is appropriate or not depends on the usecase!

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

When is it appropriate to use an associated type?

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

Similarly to the trait methods above, an associated type is a form of late binding (though it occurs at compilation), allowing the user of the trait to specify for a given instance which type to substitute. It is not the only way (thus the question):

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

或者:

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

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

Are equivalent to the late binding of methods above:

  • 第一个强制对于给定的 Self 有一个 Return 关联
  • 相反,第二个允许为多个 Return
  • Self 实现 MyTrait
  • the first one enforces that for a given Self there is a single Return associated
  • the second one, instead, allows implementing MyTrait for Self for multiple Return

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

Which form is more appropriate depends on whether it makes sense to enforce unicity or not. For example:

  • Deref 使用关联类型,因为没有 unicity 编译器会在推理过程中发疯
  • Add 使用关联类型,因为它的作者认为给定两个参数会有一个逻辑返回类型
  • Deref uses an associated type because without unicity the compiler would go mad during inference
  • Add uses an associated type because its author thought that given the two arguments there would be a logical return type

如您所见,虽然 Deref 是一个明显的用例(技术约束),但 Add 的情况不太明确:也许它对 i32 + i32 根据上下文产生 i32Complex ?尽管如此,作者还是行使了自己的判断,并认为没有必要为添加重载返回类型.

As you can see, while Deref is an obvious usecase (technical constraint), the case of Add is less clear cut: maybe it would make sense for i32 + i32 to yield either i32 or Complex<i32> depending on the context? Nonetheless, the author exercised their judgment and decided that overloading the return type for additions was unnecessary.

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

My personal stance is that there is no right answer. Still, beyond the unicity argument, I would mention that associated types make using the trait easier as they decrease the number of parameters that have to be specified, so in case the benefits of the flexibility of using a regular trait parameter are not obvious, I suggest starting with an associated type.

这篇关于什么时候使用关联类型与泛型类型合适?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆