导航
Rust RC和Ref Cell的使用
基本内容其实很多书本都有说,提取一些关键的说吧
一、RC的作用
适合于单线程中,实现对一个变量的,同时持有多个不可变引用的拥有者,拥有者的意思就是对变量是有所有权的。
Rc<T>
,对于T
,可以有多个拥有者,只有T
失去所有的拥有者,T
才可以被drop
我觉得RC
和Box
都是智能指针,只不过说RC可以在单线程内,同时持有多个不可变引用的变量。
举个例子来说明RC
和Box
关键的不同吧
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有所有权,但是通过RefCell
的borrow
,borrow_mut
借出来的不会发生所有权转移
比如
let a=r.borrow
a 不会拿走原来r拥有的值
- 仅在编译期绕过
可变引用
和不可变引用
不能共存原则。但是运行时不能破坏
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);//编译可以通过,但是运行不通过
}
- 在运行时,在不破坏
可变引用
和不可变引用
不能共存原则下,修改不可变引用指向的值,我们就可以用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
),实现对一个不可变值进行修改,
需要注意的是
Rc
不可以修改指向别的的RefCell
,可以持有多个RefCell
,这是由Rc
的特点决定的- 每个
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
}
针对上面找个问题,我们可以这样解决这个问题
- 避免不可变引用和可变引用的作用域重合
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是一样的
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"