generics - 没有类型参数的泛型类型上的泛型结构

标签 generics rust higher-kinded-types

有可能在 Rust 中做这样的事情吗?

trait Foo<T> {}

struct A;
struct B;

struct Bar<T: Foo> {
    a: T<A>,
    b: T<B>
}

我知道我可以只为 Bar 使用两个参数,但我认为必须有更好的方法来做到这一点。

我想实现一个 Graph结构。因为我不能只将节点和边绑定(bind)到他们的 parent 生命周期,所以我想要像 Rc 这样的东西。 .然而,有时可能需要一个 Graph可以从多个线程访问。所以我必须同时实现 RcArc .

就是这样Foo适合:我实现 Foo两者都适用 RcArc ( Foo 需要 Deref ),我使用参数 T绑定(bind)到 Foo .这就是我想要为单线程和多线程使用一个结构的方式。

最佳答案

⇒ 这是当前不可能 用 Rust 的类型系统来表达 ☹

幸运的是,由于 this RFC 中提出的“通用关联类型”, future 将成为可能。 .您可以在 the corresponding tracking issue 中跟踪实现和稳定的状态.

这里的重要术语是“HKT”( h 更高 k inded t ypes)。这是尚未在 Rust 中实现的类型系统的一个特性。 Haskell 提供 HKT。在 C++ 世界中,HKT 被称为“模板模板”。上面提到的泛型关联类型也是 HKT 的一种形式。

但究竟什么是 HKT?

让我们慢慢开始:我们所知道的简单类型是什么?让我们列出一些类型:i32 , bool , String .这些都是类型……您可以拥有这些类型的值(变量)。怎么样Vec<i32> ?这也是一种简单的类型!你可以有一个 Vec<i32> 类型的变量,没问题!

我们想将这些类型组合在一起;我们称这种分类为“某种类型”。如果我们想以一种非常抽象的方式(关于类型的类型)进行讨论,我们会选择其他词,在这种情况下是 kind。甚至还有一种表示类型的符号。对于上面的简单类型,我们说:这些类型的种类是

*

是的,只是一颗星星,很容易。符号在以后更有意义!

让我们搜索与简单类型不同的类型。 Mutex<HashMap<Vec<i32>, String>> ?不,它可能相当复杂,但它仍然很友好 *我们仍然可以拥有该类型的变量。

怎么样Vec ?是的,我们省略了尖括号。是的,这确实是另一种类型!我们可以有一个 Vec 类型的变量吗? ?不!什么的向量?!

这种捐赠方式为:
* -> *

这只是说:给我一个普通类型( * ),我将返回一个普通类型!给个普通型i32到这个东西( Vec ),它将返回一个普通类型 Vec<i32> !它也称为类型构造函数,因为它用于构造类型。我们甚至可以更进一步:
* -> * -> *

这有点奇怪,因为它与currying有关。对于非 Haskell 程序员来说,读起来很奇怪。但这意味着:给我两种类型,我将返回一种类型。让我们想一个例子... Result ! Result类型构造函数将返回一个具体类型 Result<A, B>在您提供两种具体类型之后 AB .

术语更高级的类型只是指所有不是 * 的类型。 ,它们是类型构造函数。

在你的例子中

当你写 struct Bar<T: Foo>你要T成为那种人 * -> * ,意思是:你可以给 T 一个类型并接收一个简单的类型。但正如我所说,这在 Rust 中还不能表达。要使用类似的语法,人们可能会想象这在 future 可以工作:
// This does NOT WORK!
struct Bar<for<U> T> where T<U>: Foo {
    a: T<A>,
    b: T<B>,
}
for<>语法借自 "higher-ranked trait bounds" (HRTB) ,今天可以用于抽象生命周期(最常用于闭包)。

链接

如果您想阅读有关此主题的更多信息,请访问以下链接:
  • Niko Matsakis' great series of blog posts discussing one possible solution (associated type constructors) to the HKT problem
  • The RFC proposing generic associated types (just a less scary name for "associated type constructors")
  • HRTB explanation


  • 奖金 :如果将实现关联的类型构造函数,您的问题的解决方案(我认为,因为没有办法测试)!

    由于 RFC 不允许通过 Rc,我们必须在我们的实现中绕道而行。直接作为类型参数。可以这么说,它没有直接介绍HKT。但是正如 Niko 在他的博客文章中所说的那样,我们可以通过使用所谓的“家族特征”,获得与具有关联类型构造函数的 HKT 相同的灵活性和功能。
    /// This trait will be implemented for marker types, which serve as
    /// kind of a proxy to get the real type.
    trait RefCountedFamily {
        /// An associated type constructor. `Ptr` is a type constructor, because
        /// it is generic over another type (kind * -> *).
        type Ptr<T>;
    }
    
    struct RcFamily;
    impl RefCountedFamily for RcFamily {
        /// In this implementation we say that the type constructor to construct
        /// the pointer type is `Rc`.
        type Ptr<T> = Rc<T>;
    }
    
    struct ArcFamily;
    impl RefCountedFamily for ArcFamily {
        type Ptr<T> = Arc<T>;
    }
    
    struct Graph<P: RefCountedFamily> {
        // Here we use the type constructor to build our types
        nodes: P::Ptr<Node>,
        edges: P::Ptr<Edge>,
    }
    
    // Using the type is a bit awkward though:
    type MultiThreadedGraph = Graph<ArcFamily>;
    

    有关更多信息,您应该真正阅读 Niko 的博客文章。困难的话题解释得足够好,甚至我都能或多或少地理解它们!

    编辑 : 我刚刚注意到 Niko 实际上使用了 Arc/Rc例如在他的博客文章中!我完全忘记了这一点,我自己想到了上面的代码……但也许我的潜意识还记得,因为我和 Niko 一样选择了几个名字。无论如何,这里是his (probably way better) take on the issue .

    关于generics - 没有类型参数的泛型类型上的泛型结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41508680/

    相关文章:

    c# - 通用接口(interface)的运行时转换

    java - 在接口(interface)扩展中指定泛型

    c# - 在重载方法中使用泛型

    rust - Rust 中的值是如何返回的?

    scala - 在 scala 中定义 Haskell FixF

    python - 如何在类型提示系统中使用通用(高级)类型变量?

    haskell - 为 Reader 实现幺半群

    java - 如何制作泛型的构造函数

    rust - 为什么当它是简单格式时,我会得到 "error: invalid channel name ' [工具链 ]'"的依赖项?

    string - 传递 `&str` 对 Rust 的性能不不利吗?