既然不变的引用可以做这项工作,为什么我们还需要RC<T>? [英] Why do we need Rc<T> when immutable references can do the job?

查看:0
本文介绍了既然不变的引用可以做这项工作,为什么我们还需要RC<T>?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为了说明Rc<T>的必要性,the Book提供了以下代码片段(剧透:它不会编译),以说明在没有Rc<T>的情况下无法启用多重所有权。

enum List {
    Cons(i32, Box<List>),
    Nil,
}

use crate::List::{Cons, Nil};

fn main() {
    let a = Cons(5, Box::new(Cons(10, Box::new(Nil))));
    let b = Cons(3, Box::new(a));
    let c = Cons(4, Box::new(a));
}

然后它声称(强调我的)

<2-2]>我们可以更改Cons的定义以保存引用,但随后必须指定生存期参数。通过指定生存期参数,我们将指定列表中的每个元素至少与整个列表一样长。借用检查器不允许我们编译let a = Cons(10, &Nil);,因为临时Nil值将在引用它之前被删除。

嗯,不完全是。以下代码段在rustc 1.52.1

下编译
enum List<'a> {
    Cons(i32, &'a List<'a>),
    Nil,
}

use crate::List::{Cons, Nil};

fn main() {
    let a = Cons(5, &Cons(10, &Nil));
    let b = Cons(3, &a);
    let c = Cons(4, &a);
}
请注意,通过获取引用,我们不再需要Box<T>间接地址来保存嵌套的List。此外,我可以将bc指向a,这将提供a多个概念所有者(实际上是借用者)。

问题:既然不可变引用可以完成此工作,为什么我们还需要Rc<T>

推荐答案

您可以非常粗略地想到一个静态证明的按关系排序,其中编译器需要证明总是的所有者在任何借用之前是有生命的,并且总是在所有借入死亡之后(a拥有String,它在b借入a,然后b死亡,然后a死亡;有效)是有生命的。对于许多用例,这是可以做到的,这是Rust使借用系统变得实用的见解。

在某些情况下,这不能静态完成。在您给出的示例中,您有点作弊,因为所有借阅都有'static-生存期;因此,'static项可以在任何内容之前或之后排序到无穷大-因此实际上从一开始就没有约束。当您使用不同的生命周期(许多List<'a>List<'b>等)时,该示例变得复杂得多。考虑到了。当您尝试将值传递给函数,而这些函数试图添加项时,此问题将变得明显。这是因为在函数内创建的值将在离开其作用域后死亡(即,当封闭的函数返回时),因此我们不能在以后保留对它们的引用,否则将出现悬空引用。

Rc出现在无法静态证明谁是原始所有者的情况下,该所有者的生命周期在任何其他所有者之前开始,在任何其他所有者(!)之后结束。一个典型的例子是从用户输入派生的图结构,其中多个节点可以引用一个其他节点。它们需要在运行时与它们所引用的节点形成一种&生于后,死于";的关系,以保证它们永远不会引用无效数据。Rc是一个非常简单解决方案,因为一个简单的计数器就可以表示这些关系。只要计数器不为零,某些在之后出生、在关系之前死亡的关系仍处于活动状态。这里的关键见解是,以什么顺序创建和终止节点并不重要,因为任何顺序都是有效的。只有两端的点-计数器达到0的点-实际上是重要的,两者之间的任何增加或减少都是相同的(0=+1+1+1-1-1-1=00=+1+1-1+1-1-1=0相同)当计数器达到零时Rc被销毁。在图形示例中,这是指节点不再被引用。这会告诉Rc(引用的最后一个节点)的所有者&qot;哦,结果是是基础节点的所有者-没有人知道!-我可以销毁它";。

这篇关于既然不变的引用可以做这项工作,为什么我们还需要RC&lt;T&gt;?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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