1.Cell
use std::cell::Cell;
#[derive(Debug)]
struct SomeStruct {
regular_field: u8,
special_field: Cell<u8>,
}
fn main() {
let my_struct = SomeStruct {
regular_field: 0,
special_field: Cell::new(1),
};
println!("{:?}",my_struct);
my_struct.special_field.set(17);
println!("{:?}",my_struct);
}
输出:
SomeStruct { regular_field: 0, special_field: Cell { value: 1 } }
SomeStruct { regular_field: 0, special_field: Cell { value: 17 } }
官方的例子简单明了,somestruct实现了内部可变性,因为my_struct没有使用mut修饰,所有refular_field不能直接赋值修改,但是special_filed却可以通过Cell结构的方法进行修改,组要注意的是Cell<T>的T需要实现Copy语义。
#[derive(Debug)]
struct other_struct {
ss:Cell<String>, //error[E0277]: the trait bound `String: Copy` is not satisfied
}
let o = other_struct {ss:Cell::new("hello".into())};
println!("{:?}",o);
// o.ss.set("world".into());
println!("{:?}",o);
没有实现Copy语义,或许可以定义这个结构体,但是具体使用结构体就会编译错误。
2.RefCell
#[derive(Debug)]
struct ref_struct {
num:i32,
rs: RefCell<String>,
}
fn main() {
let r = ref_struct {rs:RefCell::new("hello".into()),num:66};
println!("{:?}",r);
{
let mut rr = r.rs.borrow_mut();
rr.push_str(" oworld");
}
println!("{:?}",r.rs.borrow());
}
输出:
ref_struct { num: 66, rs: RefCell { value: "hello" } }
"hello world"
这个很直观了,和Cell相比,RefCell不限制RefCell<T>,T的类型。通常使用较多。
3.Rc
#[derive(Debug)]
struct struct1 {
s:String,
}
fn main() {
let mut one = struct1 {s:"hello".into()};
println!("{:?}",one);
let a1 = &mut one;
a1.s.push_str(" world");
// println!("{:?}",one);
a1.s.push_str("!");
println!("{:?}",one);
}
倘若我们将注释去掉,直接就会报编译错误,cannot borrow `one` as immutable because it is also borrowed as mutable,因为a1这个可变引用还很活跃。
但是,我们使用Rc和RefCell配合,就可以通过编译了。
#[derive(Debug)]
struct struct2 {
s:Rc<RefCell<String>>,
}
fn main() {
let one = struct2 {s:Rc::new(RefCell::new("hello".into()))};
println!("{:?}",one);
let mut a1 = one.s.borrow_mut();
a1.push_str(" world");
let a2 = one.s.borrow();
println!("{:?}",a2);
a1.push('!');
println!("{:?}",one);
}
/*
struct2 { s: RefCell { value: "hello" } }
thread 'main' panicked at D:\code\leetcode\first_class\a1.rs:18:20:
already mutably borrowed: BorrowError
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
*/
虽然,通过了编译,但是运行时还是会检查可变借用和不变借用,所以直接panic了,但是通过了编译
单独使用Rc的话,可以看作其他语言的共享所有权。
let s = String::from("hello");
println!("{:?}",s);
let s1 = s;
println!("{:?}",s1);
// println!("{:?}",s); //^ value borrowed here after move
String是没有实现Copy语言的,所有s1=s就会转移所有权,但如果使用clone的话,就不是指向同一片内存了。
这时候可以使用Rc<String>
let s = Rc::new(String::from("hello"));
let s1 = Rc::clone(&s);
println!("{:?}",s1);
unsafe {
let s_ptr = Rc::as_ptr(&s1) as *mut String;
(*s_ptr).push_str(" world");
}
println!("{:?}",s1);
println!("{:?}",s);
输出如下:
"hello"
"hello world"
"hello world"
可见,二者指向同一片内存。
4.Arc
Arc和Rc的区别就在于,Arc可以跨线程使用,它的引用计数是原子引用计数。所以可以确保对引用计数的修改是安全的,因此可以用于多线程。也就是说,假设多个线程拥有Rc的clone,它们同时结束,会同时修改主线程的Rc计数,这是不安全的。
let s1 = Arc::new(String::from("hello"));
let (sender, receiver) = std::sync::mpsc::sync_channel(0);//同步消息队列,确保两个线程同时运行到unsafe
// let s2 = Arc::clone(&s1); //两种方式clone都可以
let s2 = s1.clone();
let t1 = thread::spawn(move || {
receiver.recv().unwrap();
unsafe {
let s_ptr = Arc::as_ptr(&s2) as *mut String;
(*s_ptr).push_str(" thread");
}
println!("{:?}",s2);
});
println!("{:?}",s1);
sender.send(()).unwrap();
unsafe {
let s_ptr = Arc::as_ptr(&s1) as *mut String;
(*s_ptr).push_str(" main");
}
println!("{:?}",s1);
t1.join().unwrap();
//输出如下:
"hello"
"hello main"
"hello main thread"
但也有概率hello thread main
不管顺序如何,至少跨线程共享了一段内存,不考虑写的安全性,都可以读写。
此外,不使用裸指针的话,还没有办法直接s1.push_str("")
help: trait `DerefMut` is required to modify through a dereference, but it is not implemented for `Arc<String>`
如果是下面这样的话,就很明显了,出现了争用内存的问题,导致读写脏数据
let s1 = Arc::new(String::from("hello"));
let (sender, receiver) = std::sync::mpsc::sync_channel(0);
// let s2 = Arc::clone(&s1); //两种方式clone都可以
let s2 = s1.clone();
let t1 = thread::spawn(move || {
receiver.recv().unwrap();
unsafe {
let s_ptr = Arc::as_ptr(&s2) as *mut String;
(*s_ptr).push('t');
(*s_ptr).push('h');
(*s_ptr).push('r');
(*s_ptr).push('e');
(*s_ptr).push('a');
(*s_ptr).push('d');
}
println!("thread push {:?}",s2);
});
println!("{:?}",s1);
sender.send(()).unwrap();
unsafe {
let s_ptr = Arc::as_ptr(&s1) as *mut String;
(*s_ptr).push('m');
(*s_ptr).push('a');
(*s_ptr).push('i');
(*s_ptr).push('n');
}
println!("main push {:?}",s1);
t1.join().unwrap();
println!("last {:?}",s1);
//输出如下:
main push "hellothread"
thread push "hellothread"
last "hellothread"
push的main被完全覆盖掉了。
这时,就需要用到互斥锁了。
let s1 = Arc::new(Mutex::new(String::from("hello")));
let (sender, receiver) = std::sync::mpsc::sync_channel(0);
// let s2 = Arc::clone(&s1); //两种方式clone都可以
let s2 = s1.clone();
let t1 = thread::spawn(move || {
receiver.recv().unwrap();
s2.lock().unwrap().push_str(" thread");
println!("thread push {:?}",s2);
});
println!("{:?}",s1);
sender.send(()).unwrap();
s1.lock().unwrap().push_str(" main");
println!("main push {:?}",s1);
t1.join().unwrap();
println!("last {:?}",s1);
//输出
Mutex { data: "hello", poisoned: false, .. }
main push Mutex { data: "hello main", poisoned: false, .. }
thread push Mutex { data: "hello main thread", poisoned: false, .. }
last Mutex { data: "hello main thread", poisoned: false, .. }
通过互斥锁,实现了跨线程读写同一片内存。使用起来就和数据结构直接读写一样,不需要裸指针
或者这样读写,也是安全的。
let t1 = thread::spawn(move || {
receiver.recv().unwrap();
// s2.lock().unwrap().push_str(" thread");
{
let mut m2 = s2.lock().unwrap();
m2.push(' ');
m2.push('t');
m2.push('h');
m2.push('r');
m2.push('e');
m2.push('a');
m2.push('d');
}
println!("thread push {:?}",s2);
});
println!("{:?}",s1);
sender.send(()).unwrap();
// s1.lock().unwrap().push_str(" main");
{
let mut m1 = s1.lock().unwrap();
m1.push(' ');
m1.push('m');
m1.push('a');
m1.push('i');
m1.push('n');
}
//输出如下
Mutex { data: "hello", poisoned: false, .. }
main push Mutex { data: <locked>, poisoned: false, .. }
thread push Mutex { data: "hello main thread", poisoned: false, .. }
last Mutex { data: "hello main thread", poisoned: false, .. }
综上所述:
cell和refcell,可以实现结构体的内部可变性,分别是普通类型和所有类型。
Rc和Arc实现对同一片内存的共享所有权。但是要安全的修改,就只能在引用计数为1的时候。并且,rc用于不能跨线程,arc可以。
所以,Rc<RefCell<T>>用来实现单线程下对同一片内存的互斥修改,类似于读写锁。有可变借用就不能有不可变借用。运行时会检查借用安全。
Arc<RefCell<T>>却不能在多线程下使用,因为。
the trait `Sync` is not implemented for `RefCell<String>`
所以,多线程下对同一片内存的互斥修改,需要使用Arc<Mutex<T>>
标签:里面,s1,ptr,println,let,push,可变性,hello,Rust
From: https://www.cnblogs.com/dayq/p/18645935