Daily Rust 有关所有权和生命周期的一些问题
首先看一段代码
use std::rc::Rc;
#[derive(Debug)]
struct Node {
id: usize,
downstream: Option<Rc<Node>>,
}
impl Node {
pub fn new(id: usize) -> Self {
Self {
id,
downstream: None,
}
}
pub fn update_downstream(&mut self, downstream: Rc<Node>) {
self.downstream = Some(downstream);
}
pub fn get_downstream(&self) -> Option<Rc<Node>> {
self.downstream.as_ref().map(|v| v.clone())
}
}
fn main() {
let mut node1 = Node::new(1);
let mut node2 = Node::new(2);
let mut node3 = Node::new(3);
let node4 = Node::new(4);
node3.update_downstream(Rc::new(node4));
node1.update_downstream(Rc::new(node3));
node2.update_downstream(node1.get_downstream().unwrap());
println!("node1: {:?}, node2: {:?}", node1, node2);
}
这个是一个简单的DAG(有向无环图)的示例
在图中,我们知道会有多个节点同时指向一个节点,那我们怎么在rust中用什么表示这样的数据结构呢?
如果节点的出边定义为对另一个节点的引用,那么我们就必须保证引用的节点的生命周期不比当前节点短,也就是为了保证引用有效
但是这样做的缺点就是对于节点的生命周期,我们要自己保证,所以就很容易出现悬垂引用的情况
所以在Rust中我们用RAII的方式来处理这种多个所有者的情况
具体的,就是使用Rc,在Rc的new中,我们会把原本的node移动进去,然后用Rc来帮我们管理
可以看到,在get_downstream中,我们返回一个节点的时候会把这个节点进行clone
这其实就相当于是对Rc的增加引用技术操作
之后当引用计数为0的时候, 我们就会把原本在Rc::new中移动进去的值销毁掉
还有一个细节就是我们在设置node2的下游节点的时候,不是直接设置的node3,而是先通过node1获得了他的一个Rc,然后再添加到node2中。因为Rc的new会移动我们的node,所以之后就不能再用node3了
然后是有关生命周期的一些点
在函数返回引用的时候,当&self或者&mut self出现在参数中的时候,返回值的生命周期和它相关联
我们可以考虑一下这里的semantic,当涉及到self的东西的时候,我们很有可能是在返回这个self中的一些数据。比如HashMap的get,我们返回的value不与传入进来的参数的生命周期有关,而是与self本身有关,因为我们返回的value是存储在self中的
对于如果我们要返回函数执行过程中得到的数据,那么无论参数如何,我们一定要返回的是值,或者说是move semantic,因为如果不把函数中得到的值move出来,函数结束后他就会被销毁
生命周期的标注大多数情况下可以通过仔细思考函数的作用(含义)来得到正确结果
当我们写Rust中需要很特别的处理各种错误问题的时候,这时候应该想一想是不是设计方面,或者是自己的实现需要重构
文章评论