rust引用-借用机制还是有限制的,比如我们要在多次函数调用中修改参数、跨线程传递参数并发修改的场景,单纯使用引用-借用机制就不灵了(这种场景和引用-借用设计思想是冲突的)。这时需要借助rust提供的Rc、Arc、Cell、RefCell对机制来扩展默认的引用借用机制。
慢慢品味,std库里提供的很多实现,都是围绕引用-借用机制展开的;默认的引用-借用机制适合80%的场景,20%的场景还是需要额外的机制来扩展的(引入额外的性能开销,可能其中的15%可以通过优化设计避免)。
1、线程内
use std::rc::Rc;
use std::cell::RefCell;
fn main() {
println!("Hello, world!");
let mut param = Param::default();
param.name = "xiao ming".to_string();
let rc_param = Rc::new(param);
//Rc自带引用计数,可clone多个传给给函数作为参数,超出作用域引用计数减一至零是自动销毁
//Rc不能跨线程,要跨线程使用需要改为Arc+Mutex
let rc1 = rc_param.clone();
let rc2 = rc_param.clone();
let rc3 = rc_param.clone();
println!("{}", rc1.name);
new_value_fn1(rc2);
new_value_fn2(rc3);
//如果要在函数中修改参数的值,需要使用Rc+RecCell
let mut param2 = Param::default();
param2.name = "小红".to_string();
let rc_refcell_param = Rc::new(RefCell::new(param2));
let rc_rec_p1 = rc_refcell_param.clone();
let rc_rec_p2 = rc_refcell_param.clone();
new_value_refcell_fn1(rc_rec_p1);
new_value_refcell_fn2(rc_rec_p2);
println!("{}", rc_refcell_param.borrow().name); //小红-fn1-fn2
}
fn new_value_fn1(param: Rc<Param>){
println!("from fn1: {}", param.name);
//不让修改,这能引用
//param.is_valid = false;
}
fn new_value_fn2(param: Rc<Param>){
println!("from fn2: {}", param.name);
}
fn new_value_refcell_fn1(param: Rc<RefCell<Param>>){
let mut p = param.borrow_mut();
let new_name = p.name.clone() + "-fn1";
p.name = new_name;
p.is_valid = true;
}
fn new_value_refcell_fn2(param: Rc<RefCell<Param>>){
let mut p = param.borrow_mut();
let new_name = p.name.clone() + "-fn2";
p.name = new_name;
}
struct Param{
name: String,
age: i32,
is_valid: bool,
}
impl Default for Param{
fn default () -> Self{
Self{
name: "".to_string(),
age: 20,
is_valid: true,
}
}
}
2、跨线程
use std::thread::spawn;
use std::sync::Arc;
let mut thread_p1 = Param::default();
thread_p1.name = String::from("thread param");
let t1 = spawn(move ||{
println!("in sub thread t1:{}", thread_p1.name);
});
//变量thread_p1因为有非Copy类型String,只能在一个线程闭包内使用,如果开启线程2编译报错
//let t2 = spawn(move ||{
// println!("in sub thread t2:{}", thread_p1.name);
//});
t1.join().unwrap();
//t2.join().unwrap();
我们定义一个变量,要在多个线程闭包内使用,需要引入Arc:
let mut thread_p1 = Param::default();
thread_p1.name = String::from("thread param");
let thread_param = Arc::new(thread_p1);
let thread1_param = thread_param.clone();//clone一个跨线程的引用计数变量给线程1用
let thread2_param = thread_param.clone();//clone一个跨线程的引用计数变量给线程2用
let t1 = spawn(move ||{
println!("in sub thread t1:{}", thread1_param.name);
});
let t2 = spawn(move ||{
println!("in sub thread t2:{}", thread2_param.name);
});
t1.join().unwrap();
t2.join().unwrap();
如果我们还要在线程内修改变量,则需要Mutex介入:
let mut thread_p1 = Param::default();
thread_p1.name = String::from("thread param");
let thread_param = Arc::new(Mutex::new(thread_p1));//创建跨线程传递的可读性对象
let thread1_param = thread_param.clone();//clone一个给线程1用
let thread2_param = thread_param.clone();//clone一个给线程2用
let t1 = spawn(move ||{
let mut v1 = thread1_param.lock().unwrap();//线程1使用thread1_param,先调用lock获取对象,在作用域内是独占的,其他线程不能并行使用
v1.name = v1.name.clone() + "__" + "t1";
println!("in sub thread t1:{}", v1.name);
});
let t2 = spawn(move ||{
let mut v2 = thread2_param.lock().unwrap();//线程2使用thread2_param,先调用lock获取对象,在作用域内是独占的,其他线程不能并行使用
v2.name = v2.name.clone() + "__" + "t2";
println!("in sub thread t2:{}", v2.name);
});
t1.join().unwrap();
t2.join().unwrap();
let v3 = thread_param.lock().unwrap();//验证两个子线程执行情况 p.name is thread param__t2__t1
println!("p.name is {}", v3.name);
抛开执行开销,至少其他语言可做的事情,rust也可做到了,理论上可以平行翻译其他语言实现的模块实现。