rust各种指针

Box

首先是Box,Box很接近C系语言的指针的概念,就是把一个数据结构放到heap上,然后传递一个地址调用。

Cell和RefCell代表了Rust的一个特性:Interior Mutability,内在的可变性。

顾名思义,Cell就是一个细胞,一个单元,或者说一个容器。这个容器和Box不同,Box是一个不可变的指针,如果我们想改变一个box的值,比如这么搞:

fn main() {
   let mut x = Box::new(10i32);
   *x = 20;
   println!("{:?}", x);
}

结果会打印出来20,我们无法在不dereference的情况下修改Box里面的值,并且如果要修改的话,必须要用let mut来定义Box的绑定变量。

而使用let mut有很多不方便,因为Rust的一条铁律就是可变修改只能有一个,没有还回来之前根本不能继续修改。所以cell和refcell的存在就有了必要,我们先看cell的特点:

use std::cell::Cell;

fn main() {
   let x = Cell::new(20i32);
   let y = &x;
   let z = &x;
   y.set(40i32);
   println!("{:?}", x);
   z.set(80i32);
   println!("{:?}", x);
}

是不是很神奇?y和z都是x的不可变调用,但是都成功的修改了x的值。这就是Rust在不破坏其核心安全性的前提下,对易用性作出的妥协,让我们可以在在同一个scope里面,让一个变量可以被一个或者数个不可变借用修改。
Rust的Cell是很方便,但是问题在于不是每个类型都可以用Cell包起来。Rust的文档说的明白,只有实现了copy属性的类型才能够用cell包裹,一般来说,我们自己定义的struct和trait都是没有copy属性的,所以这种就只能用RefCell。

顾名思义,RefCell是引用单元。这个的限制就更多了:没有了威武霸气的set方法,取而代之的是borrow和borrow_mut,看名字也看得出来就是可读借用和可变借用。注意这里rust的文档特别强调了,依然需要满足可变借用的唯一性,甚至更加严格:如果存在可变借用,那么在该可变借用的作用域结束之前,那么即便是再次进行只读借用,编译器都会报错,比如下面这个代码就无法通过编译:

use std::cell::RefCell;

fn main() {
   let x = RefCell::new(20i32);

   let y = x.borrow_mut();
   let z = x.borrow();

}

看到这里可能会非常灰心失望,这么严格的限制,要RefCell何用?当然是有用的,因为RefCell的限制只是针对一个变量的,我们可以非常轻易的绕过这个限制:

use std::cell::RefCell;

fn main() {
   let x = RefCell::new(20i32);
   let z = &x;

   let y = &x;
   *z.borrow_mut() = 10;
   println!("{:?}", x);
   *y.borrow_mut() = 30;
   println!("{:?}", x);
}

所以我们可以通过其他的只读绑定的borrow_mut来修改值,然后用最初的Refcell负责输出。当然如果要抬杠,说我就是想用一个变量,同时存在多个mutable的borrow怎么办?Rust自然也有办法,在下一节rc里面会继续说这个问题

Rc<T>

Rc 并不能在线程间安全的传递。

我们需要一些类型可以让我们拥有一个值的多个有所有权的引用。通常,我 们使用 Rc<T> ,它是一个引用计数类型用以提供共享的所有权。它有一些运行时 记录来跟踪引用它的数量,也就是“引用计数”。
调用 Rc<T> 的 clone() 方法会返回一个有所有权的引用并增加其内部引用计 数。

Arc<T> ,Rust 标准的原子引用计数类型。

Arc<T> 的原子部分可以在多线程中安全的访问。

为此编译器确保了内部计数的改
变都是不可分割的操作这样就不会产生数据竞 。
本质上, Arc<T> 是一个可以让我们在线程间安全的共享所有权的类型。

use std::thread;
use std::sync::Arc;
use std::time::Duration;
fn main() {
    let mut data = Arc::new(vec![1, 2, 3]);
    for i in 0..3 {
        let data = data.clone();
        thread::spawn(move || {
            data[i] += 1;
        });
}
    thread::sleep(Duration::from_millis(50));
}


评论