gpt4 book ai didi

Rust中的智能指针:BoxRcArcCellRefCellWeak

转载 作者:我是一只小鸟 更新时间:2023-04-20 06:31:06 28 4
gpt4 key购买 nike

Rust中的智能指针是什么

智能指针(smart pointers)是一类数据结构,是拥有数据所有权和额外功能的指针。是指针的进一步发展 。

指针(pointer)是一个包含内存地址的变量的通用概念。这个地址引用,或 ” 指向”(points at)一些其 他数据 。引用以 & 符号为标志并借用了他们所 指向的值。除了引用数据没有任何其他特殊功能。它们也没有任何额外开销,所以在Rust中应用得最多.

智能指针是Rust中一种特殊的数据结构。它与普通指针的本质区别在于普通指针是对值的借用,而智能指针通常拥有对数据的所有权。并且可以实现很多额外的功能.

Rust智能指针有什么用,解决了什么问题

它提供了许多强大的抽象来帮助程序员管理内存和并发。其中一些抽象包括智能指针和内部可变性类型,它们可以帮助你更安全、更高效地管理内存,例如 Box<T>  用于在堆上分配值。 Rc<T>  是一种引用计数类型,可以实现数据的多重所有权。 RefCell<T>  提供内部可变性,可用于实现对同一数据的多个可变引用 。

它们在标准库中定义,可以用来更灵活地管理内存,智能指针的一个特点就是实现了Drop和Deref这两个trait。其中Drop trait中提供了drop方法,在析构时会去调用。Deref trait提供了自动解引用的能力,让我们在使用智能指针的时候不需要再手动解引用了 。

Rust有哪些常用智能指针

  • Box<T> 是最简单的智能指针,它允许你在堆上分配值并在离开作用域时自动释放内存。
  • Rc<T> Arc<T> 是引用计数类型,它们允许多个指针指向同一个值。当最后一个指针离开作用域时,值将被释放。 Rc<T> 不是线程安全的,而 Arc<T> 是线程安全的。

内部可变性类型允许你在不可变引用的情况下修改内部值。Rust中有几种内部可变性类型,包括 Cell<T> , RefCell<T> 和 UnsafeCell<T> .

  • Cell<T> 是一个内部可变性类型,它允许你在不可变引用的情况下修改内部值。 Cell<T> 只能用于 Copy 类型,因为它通过复制值来实现内部可变性。
  • RefCell<T> 也是一个内部可变性类型,它允许你在不可变引用的情况下修改内部值。与 Cell<T> 不同, RefCell<T> 可以用于非 Copy 类型。它通过借用检查来确保运行时的安全性。
  • UnsafeCell<T> 是一个底层的内部可变性类型,它允许你在不可变引用的情况下修改内部值。与 Cell<T> RefCell<T> 不同, UnsafeCell<T> 不提供任何运行时检查来确保安全性。因此,使用 UnsafeCell<T> 可能会导致未定义行为。

此外,Rust还提供了一种弱引用类型 Weak<T> ,它可以与 Rc<T> 或 Arc<T> 一起使用来创建循环引用。 Weak<T> 不会增加引用计数,因此它不会阻止值被释放.

Box<T>

Box<T> 是最简单的智能指针,它允许你在堆上分配值并在离开作用域时自动释放内存.

Box<T> 通常用于以下情况:

  • 当你有一个类型,但不确定它的大小时,可以使用 Box<T> 来在堆上分配内存。例如,递归类型通常需要使用 Box<T> 来分配内存。
  • 当你有一个大型数据结构并希望在栈上分配内存时,可以使用 Box<T> 来在堆上分配内存。这样可以避免栈溢出的问题。
  • 当你希望拥有一个值并只关心它的类型而不是所占用的内存时,可以使用 Box<T> 。例如,当你需要将一个闭包传递给函数时,可以使用 Box<T> 来存储闭包。

总之,当你需要在堆上分配内存并管理其生命周期时,可以考虑使用 Box<T> .

下面是一个简单的例子:

                          
                            fn 
                             main() { let  b = Box::  new(  5); println!("  b = {}",  b); }  复制代码       
                          
                        

这里定义了变量 b,其值是一个指向被分配在堆上的值 5 的 Box。这个程序会打印出 b = 5;在这个例子 中,我们可以像数据是储存在栈上的那样访问 box 中的数据。正如任何拥有数据所有权的值那样,当像 b 这样的 box 在 main 的末尾离开作用域时,它将被释放。这个释放过程作用于 box 本身(位于栈上) 和它所指向的数据(位于堆上).

但是 Box<T> 无法同时在多个地方对同一个值进行引用 。

                          
                             enum  List {  Cons(  i32,  Box), Nil, }  use crate::List::{Cons, Nil};  fn  main() {  let   a =  Cons(  5,  Box::  new(  Cons(  10,  Box::  new(Nil))));  let   b =  Cons(  3,  Box::  new(a));  let   c =  Cons(  4,  Box::  new(a)); }  复制代码                                  
                          
                        

编译会得出如下错误: error[E0382]: use of moved value: a ,因为b和c无法同时拥有a的所有权,这个时候我们要用 Rc<T> 。

Rc<T> Reference Counted 引用计数

Rc<T> 是一个引用计数类型,它允许多个指针指向同一个值。当最后一个指针离开作用域时,值将被释放。 Rc<T> 不是线程安全的,因此不能在多线程环境中使用.

Rc<T> 通常用于以下情况:

  • 当你希望在多个地方共享数据时,可以使用 Rc<T> 。解决了使用 Box<T> 共享数据时出现编译错误
  • 当你希望创建一个循环引用时,可以使用 Rc<T> Weak<T> 来实现。

下面是一个简单的例子,演示如何使用 Rc<T> 来共享数据:

                          
                             use std::rc::Rc;  fn  main() {  let   data = Rc::  new(  vec![  1,  2,  3]);  let   data1 = data.  clone();  let   data2 = data.  clone();  println!(  "data: {:?}", data);  println!(  "data1: {:?}", data1);  println!(  "data2: {:?}", data2); }  复制代码                          
                          
                        

这个例子中,我们使用 Rc::new 来创建一个新的 Rc<T> 实例。然后,我们使用 clone 方法来创建两个新的指针,它们都指向同一个值。由于 Rc<T> 实现了引用计数,所以当最后一个指针离开作用域时,值将被释放.

但是 Rc<T> 在多线程中容易引发线程安全问题,为了解决这个问题,又有了 Arc<T> 。

Arc<T> Atomically Reference Counted 原子引用计数

Arc<T> 是一个线程安全的引用计数类型,它允许多个指针在多个线程之间共享同一个值。当最后一个指针离开作用域时,值将被释放.

Arc<T> 通常用于以下情况:

  • 当你希望在多个线程之间共享数据时,可以使用 Arc<T> ,是 Rc<T> 的多线程版本。
  • 当你希望在线程之间传递数据时,可以使用 Arc<T> 来实现。

下面是一个简单的例子,演示如何使用 Arc<T> 来在线程之间共享数据:

                          
                            use std::sync::Arc
                             ; use std::thread  ; fn main() { let  data = Arc::new(vec![  1,  2,  3])  ; let  data1 = data.clone()  ; let  data2 = data.clone()  ; let  handle1 = thread::spawn(move || { println!("data1: {:?}", data1)  ; })  ; let  handle2 = thread::spawn(move || { println!("data2: {:?}", data2)  ; })  ; handle1.join().unwrap()  ; handle2.join().unwrap()  ; }  复制代码                    
                          
                        

这个例子中,我们使用 Arc::new 来创建一个新的 Arc<T> 实例。然后,我们使用 clone 方法来创建两个新的指针,它们都指向同一个值。接着,我们在线程中使用这些指针来访问共享数据。由于 Arc<T> 实现了线程安全的引用计数,所以当最后一个指针离开作用域时,值将被释放.

Weak<T> 弱引用类型

Weak<T> 是一个弱引用类型,它可以与 Rc<T> 或 Arc<T> 一起使用来创建循环引用。 Weak<T> 不会增加引用计数,因此它不会阻止值被释放.

当你希望创建一个循环引用时,可以使用 Rc<T> 或 Arc<T> 和 Weak<T> 来实现.

Weak<T> 通常用于以下情况:

  • 当你希望观察一个值而不拥有它时,可以使用 Weak<T> 来实现。由于 Weak<T> 不会增加引用计数,所以它不会影响值的生命周期.

下面是一个简单的例子,演示如何使用 Rc<T> 和 Weak<T> 来创建一个循环引用:

                          
                             use std::rc::{Rc, Weak};  struct  Node { value:  i32, next:  Option<Rc<Node>>, prev:  Option<Weak<Node>>, }  fn  main() {  let   first = Rc::  new(Node { value:  1, next:  None, prev:  None });  let   second = Rc::  new(Node { value:  2, next:  None, prev:  Some(Rc::  downgrade(&first)) }); first.next =  Some(second.  clone()); }  复制代码                          
                          
                        

这个例子中,我们定义了一个 Node 结构体,它包含一个值、一个指向下一个节点的指针和一个指向前一个节点的弱引用。然后,我们创建了两个节点 first 和 second ,并使用 Rc::downgrade 方法来创建一个弱引用。最后,我们将两个节点连接起来,形成一个循环引用.

需要注意的是,由于 Weak<T> 不会增加引用计数,所以它不会阻止值被释放。当你需要访问弱引用指向的值时,可以使用 upgrade 方法来获取一个临时的强引用。如果值已经被释放,则 upgrade 方法会返回 None .

UnsafeCell<T>

UnsafeCell<T> 是一个底层的内部可变性类型,它允许你在不可变引用的情况下修改内部值。与 Cell<T> 和 RefCell<T> 不同, UnsafeCell<T> 不提供任何运行时检查来确保安全性。因此,使用 UnsafeCell<T> 可能会导致未定义行为.

由于 UnsafeCell<T> 是一个底层类型,它通常不直接用于应用程序代码。相反,它被用作其他内部可变性类型(如 Cell<T> 和 RefCell<T> )的基础.

下面是一个简单的例子,演示如何使用 UnsafeCell<T> 来修改内部值:

                          
                            use std::cell::UnsafeCell
                             ; fn main() { let  x = UnsafeCell::new(  1)  ; let  y = &x  ; let  z = &x  ; unsafe { *x.get() = 2  ; *y.get() = 3  ; *z.get() = 4  ; } println!("x: {}", unsafe { *x.get() })  ; }  复制代码             
                          
                        

这个例子中,我们使用 UnsafeCell::new 来创建一个新的 UnsafeCell<T> 实例。然后,我们创建了两个不可变引用 y 和 z ,它们都指向同一个值。接着,在一个 unsafe 块中,我们使用 get 方法来获取一个裸指针,并使用它来修改内部值。由于 UnsafeCell<T> 实现了内部可变性,所以我们可以在不可变引用的情况下修改内部值.

需要注意的是,由于 UnsafeCell<T> 不提供任何运行时检查来确保安全性,所以使用它可能会导致未定义行为。因此,在大多数情况下,你应该使用其他内部可变性类型(如 Cell<T> 和 RefCell<T> ),而不是直接使用 UnsafeCell<T> .

Cell<T>

Cell<T> 是一个内部可变性类型,它允许你在不可变引用的情况下修改内部值。 Cell<T> 只能用于 Copy 类型,因为它通过复制值来实现内部可变性.

Cell<T> 通常用于以下情况:

  • 当你需要在不可变引用的情况下修改内部值时,可以使用 Cell<T> 来实现内部可变性。
  • 当你需要在结构体中包含一个可变字段时,可以使用 Cell<T> 来实现。 下面是一个简单的例子,演示如何使用 Cell<T> 来修改内部值:
                          
                            use std::cell::Cell
                             ; fn main() { let  x = Cell::new(  1)  ; let  y = &x  ; let  z = &x  ; x.set(2)  ; y.set(3)  ; z.set(4)  ; println!("x: {}", x.get())  ; }  复制代码             
                          
                        

这个例子中,我们使用 Cell::new 来创建一个新的 Cell<T> 实例。然后,我们创建了两个不可变引用 y 和 z ,它们都指向同一个值。接着,我们使用 set 方法来修改内部值。由于 Cell<T> 实现了内部可变性,所以我们可以在不可变引用的情况下修改内部值.

需要注意的是,由于 Cell<T> 通过复制值来实现内部可变性,所以它只能用于 Copy 类型。如果你需要在不可变引用的情况下修改非 Copy 类型的值,可以考虑使用 RefCell<T> .

RefCell<T>

RefCell<T> 是一个内部可变性类型,它允许你在不可变引用的情况下修改内部值。与 Cell<T> 不同, RefCell<T> 可以用于非 Copy 类型.

RefCell<T> 通过借用检查来确保运行时的安全性。当你尝试获取一个可变引用时, RefCell<T> 会检查是否已经有其他可变引用或不可变引用。如果有,则会发生运行时错误.

RefCell<T> 通常用于以下情况:

  • 当你需要在不可变引用的情况下修改内部值时,可以使用 RefCell<T> 来实现内部可变性.

  • 当你需要在结构体中包含一个可变字段时,可以使用 RefCell<T> 来实现.

下面是一个简单的例子,演示如何使用 RefCell<T> 来修改内部值:

                          
                            use std::cell::RefCell
                             ; fn main() { let  x = RefCell::new(vec![  1,  2,  3])  ; let  y = &x  ; let  z = &x  ; x.borrow_mut().push(4)  ; y.borrow_mut().push(5)  ; z.borrow_mut().push(6)  ; println!("x: {:?}", x.borrow())  ; }  复制代码               
                          
                        

这个例子中,我们使用 RefCell::new 来创建一个新的 RefCell<T> 实例。然后,我们创建了两个不可变引用 y 和 z ,它们都指向同一个值。接着,我们使用 borrow_mut 方法来获取一个可变引用,并使用它来修改内部值。由于 RefCell<T> 实现了内部可变性,所以我们可以在不可变引用的情况下修改内部值.

需要注意的是,由于 RefCell<T> 通过借用检查来确保运行时的安全性,所以当你尝试获取一个可变引用时,如果已经有其他可变引用或不可变引用,则会发生运行时错误。from刘金,转载请注明原文链接。感谢! 。


 

最后此篇关于Rust中的智能指针:BoxRcArcCellRefCellWeak的文章就讲到这里了,如果你想了解更多关于Rust中的智能指针:BoxRcArcCellRefCellWeak的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com