tor*_*eyy 9 generics higher-kinded-types rust
在Rust中可以做这样的事吗?
trait Foo<T> {}
struct A;
struct B;
struct Bar<T: Foo> {
a: T<A>,
b: T<B>
}
Run Code Online (Sandbox Code Playgroud)
我知道我可以使用两个参数Bar,但我认为必须有更好的方法来做到这一点.
我想实现一个Graph结构.因为我不能将节点和边缘绑定到父母的生命周期,我希望有类似的东西Rc.但是,有时可能需要Graph具有多个线程的访问权限.所以我必须同时使用Rc和Arc.
这Foo是有益的:我实现Foo两者Rc和Arc(Foo将需要Deref)并使用T绑定的参数Foo.这就是我想要一个结构用于单线程和多线程使用的方式.
Luk*_*odt 17
⇒ 目前在Rust的类型系统中无法表达☹
幸运的是,由于本RFC中提出的"通用关联类型",将来有可能实现.您可以在相应的跟踪问题中跟踪实施和稳定的状态.
这里最重要的词是"HKT"(^ h igher ķ inded 牛逼 YPES).这是类型系统的一个功能,尚未在Rust中实现.Haskell提供HKT.在C++世界中,HKT被称为"模板模板".上述通用相关类型也是HKT的一种形式.
让我们慢慢开始:我们知道什么是简单类型?让我们列举一些类型:i32,bool,String.这些都是类型......您可以拥有这些类型的值(变量).怎么样Vec<i32>?这也是一个简单的类型!你可以有一个类型的变量Vec<i32>,没问题!
我们想把这些类型组合在一起; 我们称这种分类为" 一种类型".如果我们想在一个非常抽象的方式谈论(约种类型),我们选择换言之,那种在这种情况下.甚至还有一个符号种类的类型.对于我们上面的简单类型,我们说:那些类型的类型是
*
Run Code Online (Sandbox Code Playgroud)
是的,只是一个明星,非常容易.这种符号后来更有意义!
让我们搜索与我们的简单类型不同的类型.Mutex<HashMap<Vec<i32>, String>>?不,它可能相当复杂,但它仍然是善良的*,我们仍然可以有这种类型的变量.
怎么样Vec?是的,我们省略了角括号.是的,这确实是另一种类型!我们可以有一个类型的变量Vec吗?没有!什么的矢量?!
这种捐赠为:
* -> *
Run Code Online (Sandbox Code Playgroud)
这只是说:给我一个普通的类型(*),我将返回一个普通的类型!给i32这个东西(Vec)一个正常的类型,它将返回一个普通的类型Vec<i32>!它也被称为类型构造函数,因为它用于构造类型.我们甚至可以走得更远:
* -> * -> *
Run Code Online (Sandbox Code Playgroud)
这有点奇怪,因为它与curry有关,读取非Haskell程序员的奇数.但它意味着:给我两种类型,我将返回一种类型.让我们考虑一个例子...... Result!该Result类型的构造函数会返回一个具体类型Result<A, B>你提供了两种具体类型后A和B.
术语较高的kinded类型只是指所有不*属于类型构造函数的类型.
当你写作时,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>,
}
Run Code Online (Sandbox Code Playgroud)
该for<>语法是从借来的"高排名特质界限"(HRTB) ,它目前可用于提取超过寿命(通常与封闭使用).
如果您想要阅读有关此主题的更多信息,请参阅以下链接:
额外奖励:在实现相关类型构造函数的情况下解决您的问题(我认为,因为没有办法测试)!
我们必须绕道而行,因为RFC不允许Rc直接作为类型参数传递.它没有直接介绍HKTs,可以这么说.但正如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>;
Run Code Online (Sandbox Code Playgroud)
有关更多信息,您应该阅读Niko的博客文章.困难的主题解释得很好,即使我可以或多或少地理解它们!
编辑:我刚刚注意到Niko实际上在他的博客文章中使用了Arc/ Rcexample!我完全忘记了并且想到了我自己上面的代码......但也许我的潜意识仍然被记住,因为我选择了几个与Niko完全相同的名字.无论如何,这是他(可能更好)对这个问题的看法.
在某种程度上Rust 确实看起来很像HKT(参见Lukas的答案,可以很好地描述它们是什么),尽管有一些可以说是笨拙的语法.
首先,您需要为所需的指针类型定义接口,这可以使用通用特征来完成.例如:
trait SharedPointer<T>: Clone {
fn new(v: T) -> Self;
// more, eg: fn get(&self) -> &T;
}
Run Code Online (Sandbox Code Playgroud)
加上一个通用特征,它定义了一个你真正想要的类型的关联类型,它必须实现你的接口:
trait Param<T> {
type Pointer: SharedPointer<T>;
}
Run Code Online (Sandbox Code Playgroud)
接下来,我们为我们感兴趣的类型实现该接口:
impl<T> SharedPointer<T> for Rc<T> {
fn new(v: T) -> Self {
Rc::new(v)
}
}
impl<T> SharedPointer<T> for Arc<T> {
fn new(v: T) -> Self {
Arc::new(v)
}
}
Run Code Online (Sandbox Code Playgroud)
并定义一些实现上述Param特征的虚拟类型.这是关键部分; 我们可以有一个type(RcParam)实现Param<T>任何类型T,包括能够提供一个类型,这意味着我们正在模拟一个更高级的类型.
struct RcParam;
struct ArcParam;
impl<T> Param<T> for RcParam {
type Pointer = Rc<T>;
}
impl<T> Param<T> for ArcParam {
type Pointer = Arc<T>;
}
Run Code Online (Sandbox Code Playgroud)
最后我们可以使用它:
struct A;
struct B;
struct Foo<P: Param<A> + Param<B>> {
a: <P as Param<A>>::Pointer,
b: <P as Param<B>>::Pointer,
}
impl<P: Param<A> + Param<B>> Foo<P> {
fn new(a: A, b: B) -> Foo<P> {
Foo {
a: <P as Param<A>>::Pointer::new(a),
b: <P as Param<B>>::Pointer::new(b),
}
}
}
fn main() {
// Look ma, we're using a generic smart pointer type!
let foo = Foo::<RcParam>::new(A, B);
let afoo = Foo::<ArcParam>::new(A, B);
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
3162 次 |
| 最近记录: |