是否可以在Rust中做类似的事情?
trait Foo<T> {}
struct A;
struct B;
struct Bar<T: Foo> {
a: T<A>,
b: T<B>
}
我知道我可以只为
Bar
使用两个参数,但是我认为必须有一种更好的方法来做到这一点。我想实现一个
Graph
结构。由于我不能仅将节点和边缘绑定(bind)到其父辈,因此我想使用Rc
之类的东西。但是,有时可能需要带有多个线程访问权限的Graph
。因此,我必须同时使用Rc
和Arc
来实现。那就是
Foo
的优点:我为Foo
和Rc
都实现了Arc
(Foo
需要Deref
),并且我使用了绑定(bind)到T
的参数Foo
。这就是我想要为单线程和多线程使用提供一个结构的方式。
最佳答案
⇒这是,目前无法在Rust的类型系统中表达☹
幸运的是,由于this RFC中提出的“通用关联类型”,将来有可能实现。您可以在the corresponding tracking issue中跟踪实现和稳定的状态。
这里的重要术语是“HKT”( h igher k inded t ypes)。它是Rust中尚未实现的类型系统的功能。 Haskell提供HKT。在C++世界中,HKT被称为“模板模板”。上述通用关联类型也是HKT的一种形式。
但是,香港电汇真的是什么?
让我们慢慢开始:我们知道什么是简单类型?让我们列出一些类型:i32
,bool
,String
。这些都是类型...您可以拥有这些类型的值(变量)。那Vec<i32>
呢?这也是一个简单的类型!您可以使用Vec<i32>
类型的变量,没问题!
我们希望将这些类型组合在一起;我们称这种分类为“一种类型”。如果我们想以非常抽象的方式谈论(关于类型的类型),我们选择其他单词,在这种情况下,请选择“亲切”。甚至对各种类型都有一种表示法。对于上面的简单类型,我们说:这些类型的种类是
*
是的,只是一颗星星,非常容易。稍后,该符号更有意义!
让我们搜索与我们的简单类型不同类型的类型。
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中尚无法表达。要使用类似的语法,可能会想到这将来可能会起作用:// 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/64995964/