有可能在 Rust 中做这样的事情吗?
trait Foo<T> {}
struct A;
struct B;
struct Bar<T: Foo> {
a: T<A>,
b: T<B>
}
我知道我可以只为
Bar
使用两个参数,但我认为必须有更好的方法来做到这一点。我想实现一个
Graph
结构。因为我不能只将节点和边绑定(bind)到他们的 parent 生命周期,所以我想要像 Rc
这样的东西。 .然而,有时可能需要一个 Graph
可以从多个线程访问。所以我必须同时实现 Rc
和 Arc
.就是这样
Foo
适合:我实现 Foo
两者都适用 Rc
和 Arc
( 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>
在您提供两种具体类型之后 A
和 B
.术语更高级的类型只是指所有不是
*
的类型。 ,它们是类型构造函数。在你的例子中
当你写
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) ,今天可以用于抽象生命周期(最常用于闭包)。链接
如果您想阅读有关此主题的更多信息,请访问以下链接:
奖金 :如果将实现关联的类型构造函数,您的问题的解决方案(我认为,因为没有办法测试)!
由于 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/