在 rust 迹斑斑的地方有可能做这样的事情吗?

trait Foo<T> {}

struct A;
struct B;

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

我知道我可以用两个参数来表示Bar,但我认为必须有更好的方法来实现这一点.

我想实现一个Graph struct .因为我不能只将 node 和边绑定到它们的父生命周期,所以我想要像Rc这样的东西.然而,有时可能需要一个Graph,可以从多个线程访问.所以我必须有一个RcArc的实现.

这就是Foo的好处:我为RcArc实现了Foo(Foo需要Deref),并且我使用绑定到Foo的参数T.这就是我希望单线程和多线程使用一个 struct 的原因.

推荐答案

⇒ 这是currently impossible在Rust的类型系统中表示的☹

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


这里的重要术语是"HKT"(100igher 101inded 102ypes).这是尚未在Rust中实现的类型系统的一个功能.Haskell提供HKTs.在C++世界中,HTTS被称为"模板模板".上述泛型关联类型也是HKTs的一种形式.

但什么是HKT,真的吗?

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

我们想把这些类型组合在一起;我们称这种分类为"类型kind".如果我们想以一种非常抽象的方式谈论(关于类型的类型),我们可以 Select 其他单词,在本例中是kind.甚至还有kinds种类型的符号.对于上面的简单类型,我们说:这些类型是

*

是的,只是一颗星星,很简单.这个符号以后会更有意义!


让我们搜索与简单类型不同的类型.Mutex<HashMap<Vec<i32>, String>>? 不,它可能相当复杂,但它仍然是*类,我们仍然可以有一个这种类型的变量.

Vec美元怎么样?是的,我们省略了尖括号.是的,这确实是另一种类型!我们能有一个Vec型的变量吗?不what的向量?!

这类捐款的形式如下:

* -> *

这只是说:给我一个正常类型(*),我会返回一个正常类型!给这个东西(Vec)一个普通类型i32,它就会返回一个普通类型Vec<i32>!它也称为type constructor,因为它用于构造类型.我们甚至可以更进一步:

* -> * -> *

这有点奇怪,因为它与currying有关,对于非Haskell程序员来说是奇数.但这意味着:给我two种类型,我会返回一种类型.让我们考虑一个例子...Result! 在您提供了两种混凝土类型AB之后,Result类型构造函数将返回一种混凝土类型Result<A, B>.

术语higher kinded types只是指所有类型,它们不是*,而是类型构造函数.

在你的例子中

当你写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)中borrow 而来的,现在可以使用"higher-ranked trait bounds" (HRTB)在生命周期内进行抽象(最常用于闭包).

链接

如果你想阅读更多关于这个话题的内容,这里有一些链接:


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

我们必须绕道实现,因为RFC不允许直接将Rc作为类型参数传递.可以说,它没有直接引入HKT.但正如Niko在他的博客文章中所说,通过使用所谓的"家庭特征",我们可以拥有与HKTs相同的灵活性和力量,以及相关的类型构造器.

/// 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的博客帖子.困难的话题解释得足够好,甚至连我都能或多或少地理解!

EDIT:我刚刚注意到Niko在他的博客文章中使用了Arc/Rc的例子!我完全忘记了这一点,想到了我上面的代码...但也许我的潜意识还记得,因为我 Select 了几个名字,就像尼科一样.不管怎样,这是his (probably way better) take on the issue美元.

Rust相关问答推荐

trait 中self 的显式生命周期似乎导致E0499无法在循环中多次borrow * emits 器作为可变的

访问Rust中的隐藏变量

Rust:跨多个线程使用hashmap Arc和rwlock

为什么reqwest以文本形式下载二进制文件?

编译项目期间使用Cargo生成时出现rustc错误

如何实现Serde::Ser::Error的调试

我无法理解Rust范围的定义(Rust Programming Language,第二版克拉布尼克和尼科尔斯)

在运行特定测试时,如何 suppress cargo test 的空输出?

为什么 vec![Vec::with_capacity(n)] 为子向量创建 0 容量?

类型生命周期绑定的目的是什么?

当没有实际结果时,如何在 Rust 中强制执行错误处理?

如何基于常量在Rust中跳过一个测试

如何使用泛型满足 tokio 异步任务中的生命周期界限

rust 中不同类型的工厂函数

在 Rust 中,将可变引用传递给函数的机制是什么?

为什么具有 Vec 变体的枚举没有内存开销?

预期的整数,找到 `&{integer}`

有没有办法阻止 rust-analyzer 使非活动代码变暗?

有没有更好的方法来为拥有 DIsplay 事物集合的 struct 实现 Display?

如何从 Rust 中不同类型的多个部分加入 Path?