Rust Rc和RefCell,看这一篇就够了~

Rust RC和Ref Cell的使用

基本内容其实很多书本都有说,提取一些关键的说吧

一、RC的作用

适合于单线程中,实现对一个变量的,同时持有多个不可变引用的拥有者,拥有者的意思就是对变量是有所有权的。

Rc<T>,对于T,可以有多个拥有者,只有T失去所有的拥有者,T才可以被drop

我觉得RCBox都是智能指针,只不过说RC可以在单线程内,同时持有多个不可变引用的变量。

举个例子来说明RCBox关键的不同吧

Box

fn main() {
  let a =Box::new (String::from("value"));
  let b=a;
  println!("{},{}",a,b);//报错,Box::new (1)的所有权已经交给B

}

RC

fn main() {
    let a =Rc::new (String::from("value"));
    let b=a.clone();
    println!("{},{}",a,b);

}

二、RefCell的作用

上面说到RC能够实现对同一个变量,同时持有多个不可变引用

那么如果我想满足下面列举1和2的需求,那么我们可以用RefCell

RefCell可以在编译器绕过借用规则的限制,可变引用不可变引用不能共存,RefCell有所有权,但是通过RefCellborrowborrow_mut 借出来的不会发生所有权转移
比如
let a=r.borrow
a 不会拿走原来r拥有的值

  1. 仅在编译期绕过可变引用不可变引用不能共存原则。但是运行时不能破坏
use std::cell::RefCell;

fn main() {
    let a = String::from("hello, world");
    let s = RefCell::new(a);
    let s1 = s.borrow();
    let s2 = s.borrow_mut();

    println!("{},{}", s1, s2);//编译可以通过,但是运行不通过
}
  1. 运行时,在不破坏可变引用不可变引用不能共存原则下,修改不可变引用指向的值,我们就可以用RefCell。
use std::cell::RefCell;
fn main() {
    let a = String::from("hello, world");//这是一个不可变字符串
    let s = RefCell::new(a);
  
    let  mut s2 = s.borrow_mut();//获得不可变变量的可变引用,
    s2.push_str("!!!!!");//可变引用成功修改不可变变量的值
    println!("{}",  s2);
}

运行一下

hello, world!!!!!

三、Rc和RefCell 组合使用

1、Rc::new(RefCell::new(a))

组合之后,RC持有多个不可变引用(RefCell),实现对一个不可变值进行修改,

需要注意的是

  1. Rc可以修改指向别的的RefCell,可以持有多个RefCell ,这是由Rc的特点决定的
  2. 每个Rc持有的RefCell 都也可以内部修改值,但是要在运行时保证借用原则
use std::cell::Cell;
use std::cell::RefCell;
use std::rc::Rc;
fn main() {
    let a=String::from("value");//不可变变量
    let s = Rc::new(RefCell::new(a));
	//RefCell 持有一个不可变变量的引用,可以实现对a的修改
	//Rc::new(RefCell) 可以拥有多个RefCell
    let s1 = s.clone();
    let s2 = s.clone();

    // let mut s2 = s.borrow_mut();
    s2.borrow_mut().push_str(", oh yeah!");
    
    println!("s:{:?}\ns1:{:?}\ns2:{:?}", s, s1, s2);//注意s1的打印也变了
}

代码输出

s:RefCell { value: "value, oh yeah!" }
s1:RefCell { value: "value, oh yeah!" }
s2:RefCell { value: "value, oh yeah!" }

使用注意点,我们组合使用的时候,RefCell在运行时,是不能够破坏可变引用不可变引用不能同时存在的原则的,如果破坏了,也是会panic的,我们来看一个例子。

use std::cell::Cell;
use std::cell::RefCell;
use std::rc::Rc;
fn main() {
    let a=String::from("value");
    let s = Rc::new(RefCell::new(a));

    let s1 = s.clone();
    let s2 = s.clone();

    let mut s3 = s.borrow_mut();
    let s4=s.borrow();
    s3.push_str("!!!")
    //编译通过,但是运行失败
    //编译通过的原因就是RefCell的特点,能绕过编译器的借用检查
    //运行失败的原因,是因为运行时,同时存在a的可变引用s3和不可变引用s4
    
  
}

针对上面找个问题,我们可以这样解决这个问题

  1. 避免不可变引用和可变引用的作用域重合
use std::cell::Cell;
use std::cell::RefCell;
use std::rc::Rc;

fn main() {
    let a = String::from("value");
    let s = Rc::new(RefCell::new(a));

    let s1 = s.clone();
    let s2 = s.clone();
    {
    let mut s3 = s.borrow_mut();
    s3.push_str("!!!");
    }//s3结束
    
    let s4 = s.borrow();
    println!("{}", s4);
}
  1. 避免可变引用和不可变引用生命周期重合,其实和1是一样的
fn main() {
    let a = String::from("value");
    let s = Rc::new(RefCell::new(a));

    let s1 = s.clone();
    let s2 = s.clone();
    
  
    s.borrow_mut().push_str("!!!");//可变引用生命周期在这个表达式就结束了
    let s4 = s.borrow();
    println!("{}", s4);
}

2、RefCell::new(Rc::new(a))

组合之后,
RefCell指向一个可修改的变量,即Rc,也就是说RefCell可以指向别的Rc
Rc::new(a)表示,a可以被多个不可变引用持有。

组合起来,假设代码是

let s=RefCell::new(Rc::new(a));

s这个变量,指向一个地方,并且指向的地方可以修改,并且s指向的这个地方,也可以被别人指向。类似图的链表结构吧。我们来看一个例子

use std::cell::RefCell;
use std::rc::Rc;


fn main() {
    let a=String::from("hello");
    let b=String::from("world");

    let c=Rc::new(a);
    let c1=c.clone();
    //c和c1同时持有变量a的不可变引用
    let s=RefCell::new(c.clone());
    println!("{:?}",s);
    *s.borrow_mut()=Rc::new(b);//s重新指向别的Rc
    println!("{:?},{:?}",s,c1);
}

运行结果

RefCell { value: "hello" }
RefCell { value: "world" },"hello"

在这里插入图片描述

  • 4
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值