特质的泛型类型和通用的相关类型之间有什么区别?

问题描述 投票:7回答:2

在Rust中提供通用关联类型之前会询问此问题,尽管它们是proposeddeveloped

我的理解是,特质泛型和相关类型在它们可以绑定到结构的类型数量上有所不同。

泛型可以绑定任意数量的类型:

struct Struct;

trait Generic<G> {
    fn generic(&self, generic: G);
}

impl<G> Generic<G> for Struct {
    fn generic(&self, _: G) {}
}

fn main() {
    Struct.generic(1);
    Struct.generic("a");
}

关联类型只绑定1种类型:

struct Struct;

trait Associated {
    type Associated;

    fn associated(&self, associated: Self::Associated);
}

impl Associated for Struct {
    type Associated = u32;

    fn associated(&self, _: Self::Associated) {}
}

fn main() {
    Struct.associated(1);
    // Struct.associated("a"); // `expected u32, found reference`
}

通用关联类型是这两者的混合。它们绑定到一个类型正好相关的1个生成器,而这个生成器又可以关联任意数量的类型。那么前面例子中的Generic和这个通用关联类型有什么区别?

struct Struct;

trait GenericAssociated {
    type GenericAssociated;

    fn associated(&self, associated: Self::GenericAssociated);
}

impl<G> GenericAssociated for Struct {
    type GenericAssociated = G;

    fn associated(&self, _: Self::GenericAssociated) {}
}
generics rust generic-programming
2个回答
8
投票

让我们再看看你的最后一个例子(由我缩短):

trait GenericAssociated {
    type GenericAssociated;
}

impl<G> GenericAssociated for Struct {
    type GenericAssociated = G;
}

这不具有通用关联类型!您只是在impl块上有一个泛型类型,您将其分配给关联类型。嗯,好的,我可以看到混乱的来源。

“类型参数G不受impl trait,self type或谓词约束”的示例错误。实施GAT时,这不会改变,因为这与GAT无关。

在您的示例中使用GAT可能如下所示:

trait Associated {
    type Associated<T>; // <-- note the `<T>`! The type itself is 
                        //     generic over another type!

    // Here we can use our GAT with different concrete types 
    fn user_choosen<X>(&self, v: X) -> Self::Associated<X>;
    fn fixed(&self, b: bool) -> Self::Associated<bool>;
}

impl Associated for Struct {
    // When assigning a type, we can use that generic parameter `T`. So in fact,
    // we are only assigning a type constructor.
    type Associated<T> = Option<T>;

    fn user_choosen<X>(&self, v: X) -> Self::Associated<X> {
        Some(x)
    }
    fn fixed(&self, b: bool) -> Self::Associated<bool> {
        Some(b)
    }
}

fn main() {
    Struct.user_choosen(1);    // results in `Option<i32>`
    Struct.user_choosen("a");  // results in `Option<&str>`
    Struct.fixed(true);        // results in `Option<bool>`
    Struct.fixed(1);           // error
}

但是回答你的主要问题:

特质的泛型类型和通用的相关类型之间有什么区别?

简而言之:它们允许延迟具体类型(或寿命)的应用,这使得整个类型系统更加强大。

the RFC中有许多动机示例,最值得注意的是流迭代器和指针族示例。让我们快速了解为什么无法使用特征上的泛型实现流式迭代器。

流式迭代器的GAT版本如下所示:

trait Iterator {
    type Item<'a>;
    fn next(&self) -> Option<Self::Item<'_>>;
}

在当前的Rust中,我们可以将life参数放在trait而不是关联的类型上:

trait Iterator<'a> {
    type Item;
    fn next(&'a self) -> Option<Self::Item>;
}

到目前为止一切顺利:所有迭代器都可以像以前一样实现这个特性。但是如果我们想要使用呢?

fn count<I: Iterator<'???>>(it: I) -> usize {
    let mut count = 0;
    while let Some(_) = it.next() {
        count += 1;
    }
    count
}

我们应该注释什么生命周期?除了注释'static的生命周期,我们有两个选择:

  • fn count<'a, I: Iterator<'a>>(it: I):这不起作用,因为调用者选择函数的泛型类型。但it(将在self电话中成为next)生活在我们的堆栈框架中。这意味着调用者不知道it的生命周期。因此我们得到了一个编译器(Playground)。这不是一个选择。
  • fn count<I: for<'a> Iterator<'a>>(it: I)(使用HRTBs):这似乎有效,但它有微妙的问题。现在我们要求I为任何一生Iterator实施'a。这对于许多迭代器来说不是问题,但是一些迭代器会返回永远不会生命的项目,因此它们无法在任何生命周期内实现Iterator - 只比生命周期短。使用这些排名较高的特质界限往往导致秘密的'static边界,这是非常有限的。所以这也并不总是有效。

正如你所看到的:我们无法正确记下I的界限。实际上,我们甚至不想在count函数的签名中提及它的生命周期!它没有必要。这正是GAT允许我们做的事情(除其他事项外)。通过GAT,我们可以写:

fn count<I: Iterator>(it: I) { ... }

它会起作用。因为“具体生命的应用”只发生在我们称之为next时。

如果您对更多信息感兴趣,可以查看my blog post “ Solving the Generalized Streaming Iterator Problem without GATs”,我尝试在特征上使用泛型类型来解决缺乏GAT问题。而且(剧透):它通常不起作用。


8
投票

What's the difference?

通用关联类型(GAT)是相关类型,它们本身是通用的。 RFC以motivating example开头,强调我的:

考虑以下特征作为代表性激励示例:

trait StreamingIterator {
    type Item<'a>;
    fn next<'a>(&'a mut self) -> Option<Self::Item<'a>>;
}

这个特性非常有用 - 它允许一种迭代器产生的值,这些值的寿命与传递给next的引用的生命周期相关。该特征的一个特别明显的用例是在向量上的迭代器,其在每次迭代时产生重叠的,可变的子序列。使用标准的Iterator接口,这样的实现将是无效的,因为每个切片都需要与迭代器一样长,而不是像next启动的借用一样长。

这种特性不能用Rust表达,因为它依赖于一种更高级的多态性。此RFC将扩展Rust以包括特定形式的高级多态性,这里将其称为关联类型构造函数。此功能有许多应用程序,但主要应用程序与StreamingIterator特征的行相同:定义特征,这些特征产生的类型的生命周期与接收器类型的本地借用相关联。

请注意关联类型Item如何具有通用生命周期'a。 RFC中的大多数示例都使用生命周期,但也有an example using a generic type

trait PointerFamily {
    type Pointer<T>: Deref<Target = T>;
    fn new<T>(value: T) -> Self::Pointer<T>;
}

请注意关联类型Pointer如何具有泛型类型T

Your specific example

上一个示例中的Generic与此通用关联类型之间有什么区别

可能没有,GAT的存在对你的情况没有帮助,这似乎不需要一个本身就是通用的相关类型。

© www.soinside.com 2019 - 2024. All rights reserved.