通用结构不包含类型参数的泛型类型 [英] Generic struct over a generic type without type parameter

查看:110
本文介绍了通用结构不包含类型参数的泛型类型的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

  trait Foo< T>有人可能在Rust中做这样的事吗? {} 

struct A;
struct B;

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

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



我想实现一个 Graph 结构。因为我不能只将节点和边缘绑定到父母的生命周期中,所以我想要类似 Rc 。但是,有时可能需要一个 Graph 来访问多个线程。所以我必须同时使用 Rc Arc



这就是 Foo 的好处:我为实现 Foo Rc Arc Foo 需要 Deref ),并使用绑定到 Foo 的参数 T 。这就是我想如何为单线程和多线程使用一个结构。

解决方案

⇒目前不可能在Rust的类型系统中表达☹



这里的重要术语是HKT( h igher k 包含 t ypes )。这是一个尚未在Rust中实现的类型系统的功能。 Haskell提供HKTs。在C ++世界中,HKT被称为模板模板。

但HKT真的是什么?



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

我们希望将这些类型组合在一起;我们称这个分类为类型的类型。如果我们想以非常抽象的方式谈论(关于类型的类型),我们选择其他词语,在这种情况下是 。甚至还有 类型的符号。对于我们上面的简单类型,我们说:这些类型的类型是

  * 

是的,只是一个明星,非常容易。这个符号在后面会更有意义!






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



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

这种捐赠方式如下:

  *  - > * 

这只是说:给我一个正常的类型( * ),我将返回一个正常的类型!给这个东西( Vec )提供一个正常类型 i32 ,它将返回一个普通类型 VEC< I32> !它也被称为类型构造函数,因为它用于构造类型。我们甚至可以走得更远:

  *  - > *  - > * 

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



术语 指的是不是 * 的所有类型,它们是类型构造函数。 在你的例子中

当你编写 struct Bar< T:Foo> 您希望 T 为类型 * - > * ,意思是:你可以给一个类型 T 并接收一个简单的类型。但正如我所说,这在Rust中还不能表达。要使用类似的语法,人们可能会想象,这可能会在未来发挥作用:

  //这不行! 
struct Bar< for< U> T> T< U>:Foo {
a:T A,
b:T B,
}

<>< code>语法的是从排名较高的特征边界(HRTB),今天可用于抽象生命周期(最常用于闭包)。



链接



如果您想了解更多关于此主题的信息,可以点击以下链接:



< hr>

奖金:在相关类型构造函数被执行的情况下解决您的问题(我想,因为无法测试)! p>

因为RFC不允许直接传递 Rc 作为类型参数,所以我们必须在实现中绕行。可以这么说,它并没有直接介绍香港电讯。但正如Niko在他的博客文章中所说的,我们可以通过使用所谓的家庭特征,与HKT和相关类型的构造函数一样具有相同的灵活性和权力。

  ///此特征将用于标记类型,它用作
///类型的代理来获取实际类型。
trait RefCountedFamily {
///关联的类型构造函数。 `Ptr`是一个类型构造函数,因为
///它是另一种类型的通用类型(kind * - > *)。
type Ptr< T>;
}

struct RcFamily;
impl RefCountedFamily for RcFamily {
///在此实现中,我们说构造
的类型构造函数///指针类型是`Rc`。
type Ptr< T> = Rc< T>
}

结构体ArcFamily;
impl RefCountedFamily for ArcFamily {
type Ptr< T> = Arc< T>;
}

结构图< P:RefCountedFamily> {
//这里我们使用类型构造函数来构建我们的类型
节点:P :: Ptr< Node> ;,
edges:P :: Ptr< Edge> ;,
}

//使用类型有点笨拙:
类型MultiThreadedGraph =图< ArcFamily>;

有关更多信息,您应该阅读Niko的博客文章。难以解释的主题已经很好地解释了,即使我可以或多或少地理解它们!



编辑:我刚注意到Niko实际上使用 Arc / Rc 在他的博客文章中的示例!我完全忘记了这一点,并想到了自己的代码......但也许我的潜意识仍然记得,因为我完全按照Niko的名字选择了几个名字。无论如何,这里是他(可能会更好)承担这个问题


Is it possible to do something like this in Rust?

trait Foo<T> {}

struct A;
struct B;

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

I know I could just use two parameters for Bar, but I think there has to be a better way to do this.

I want to implement a Graph structure. As I can't just bind the nodes and edges to their parents lifetime, I want to have something like Rc. However, sometimes one may need a Graph with access from multiple threads. So I'd have to have both an implementation with Rc and Arc.

That's what Foo is good for: I implement Foo for both Rc and Arc (Foo would require Deref) and I use a parameter T bound to Foo. That's how I wanted to have one struct for single thread and multi thread usage.

解决方案

⇒ This is currently impossible to express in Rust's type system ☹

The important term here is "HKT" (higher kinded types). It's a feature of a type system which is not yet implemented in Rust. Haskell offers HKTs. In the C++ world HKTs are known as "template templates".

But what are HKTs, really?

Let's start slowly: what is a simple type as we know it? Let's list some types: i32, bool, String. These are all types... you can have a value (variable) of these types. What about Vec<i32>? It's also a simple type! You can have a variable of type Vec<i32>, no problem!

We want to group these types together; we call this categorisation a "kind of a type". If we want to talk in a very abstract way (about types of types) we choose other words, kind in this case. There is even a notation for kinds of types. For our simple types from above, we say: the kind of those types is

*

Yes, just a star, very easy. The notation makes more sense later!


Let's search for types that are of a different kind than our simple types. Mutex<HashMap<Vec<i32>, String>>? Nope, it's fairly complex maybe, but it's still of kind * and we still can have a variable of that type.

What about Vec? Yes, we omitted the angle-brackets. Yes, this is indeed another kind of type! Can we have a variable of type Vec? No! A vector of what?!

This kind is donated as:

* -> *

This just says: give me a normal type (*) and I will return a normal type! Give a normal type i32 to this thing (Vec) and it will return a normal type Vec<i32>! It's also called a type constructor, because it is used to construct types. We can even go further:

* -> * -> *

This is a bit strange, because it has to do with currying and reads odd for a non-Haskell programmer. But it means: give me two types and I will return a type. Let's think about an example... Result! The Result type constructor will return a concrete type Result<A, B> after you provided two concrete types A and B.

The term higher kinded types just refers to all kinds of types which are not *, which are type constructors.

In your example

When you write struct Bar<T: Foo> you want T to be of the kind * -> *, meaning: you can give one type to T and receive a simple type. But as I said, this is not yet expressible in Rust. To use a similar syntax, one might imagine that this could work in the future:

// This does NOT WORK!
struct Bar<for<U> T> where T<U>: Foo {
    a: T<A>,
    b: T<B>,
}

The for<> syntax is borrowed from "higher-ranked trait bounds" (HRTB), which can be used today for abstracting over lifetimes (most commonly used with closures).

Links

In case you want to read more about this topic, here are some links:


Bonus: the solution to your problem in case associated type constructors will be implemented (I think, as there is no way to test)!

We have to take a detour in our implementation since the RFC wouldn't allow to pass Rc as a type parameter directly. It doesn't introduce HKTs directly, so to speak. But as Niko argues in his blog post, we can have the same flexibility and power as HKTs with associated type constructors by using so called "family traits".

/// 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>;

For more information, you should really read Niko's blog posts. Difficult topics explained well enough, that even I can understand them more or less!

EDIT: I just noticed that Niko actually used the Arc/Rc example in his blog post! I totally forgot that and thought of the code above myself... but maybe my subconscious still remembered, as I choose a few names exactly as Niko did. Anyway, here is his (probably way better) take on the issue.

这篇关于通用结构不包含类型参数的泛型类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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