Rust中的智能指針:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak<T>

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結構體,它包含一個值、一個指向下一個節點的指針和一個指向前一個節點的弱引用。然后,我們創建了兩個節點firstsecond,并使用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>實例。然后,我們創建了兩個不可變引用yz,它們都指向同一個值。接著,在一個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>實例。然后,我們創建了兩個不可變引用yz,它們都指向同一個值。接著,我們使用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>實例。然后,我們創建了兩個不可變引用yz,它們都指向同一個值。接著,我們使用borrow_mut方法來獲取一個可變引用,并使用它來修改內部值。由于RefCell<T>實現了內部可變性,所以我們可以在不可變引用的情況下修改內部值。

需要注意的是,由于RefCell<T>通過借用檢查來確保運行時的安全性,所以當你嘗試獲取一個可變引用時,如果已經有其他可變引用或不可變引用,則會發生運行時錯誤。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/35840.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/35840.shtml
英文地址,請注明出處:http://en.pswp.cn/news/35840.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

UML 類圖的畫法

1.類圖的畫法 類 整體是個矩形&#xff0c;第一層類名&#xff0c;第二層屬性&#xff0c;第三層方法。 &#xff1a;public- : private# : protected空格: 默認的default 對應的類寫法。 public class Student {public String name;public Integer age;protected I…

2023杭電第七場補題報告1002 1004 1011 1013

2023杭電第七場補題報告1002 1004 1011 1013 1002 B. Random Nim Game (hdu.edu.cn) 思路 手推一下就可以發現其實除了一次必定結束的其他情況概論都是 1 2 \frac{1}{2} 21? 代碼 #include <bits/stdc.h> using namespace std; #define int long long void solve()…

【hello C++】特殊類設計

目錄 一、設計一個類&#xff0c;不能被拷貝 二、設計一個類&#xff0c;只能在堆上創建對象 三、設計一個類&#xff0c;只能在棧上創建對象 四、請設計一個類&#xff0c;不能被繼承 五、請設計一個類&#xff0c;只能創建一個對象(單例模式) C&#x1f337; 一、設計一個類&…

Sentinel使用實例

不說了&#xff0c;直接上官方文檔 https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-examples/sentinel-example/sentinel-core-example/readme-zh.md Sentinel Example 項目說明 本項目演示如何使用 Sentinel starter 完成 Spring Clo…

【金融量化】對企業進行估值的方法有哪些?

估值的方法有哪些&#xff1f; 如何對企業進行估值&#xff1f;有2個方法估算。 1 絕對估值法 它是一種定價模型&#xff0c;用于計算企業的內在價值。 比如說你可以根據公司近N年的現金流情況。借此去預測未來N年的現金流情況。所有的現金流數據都可以在年報上查詢到。最后…

ios 知識

IOS 類文件.h和.m中interface的區別 大家都知道我們在創建類文件時會發現&#xff1a; #import <UIKit/UIKit.h>interface ViewController : UIViewControllerend和 #import "ViewController.h"interface ViewController ()end那么他們之間有何區別呢&#x…

【Ajax】回調地獄解決方法

回調地獄&#xff08;Callback Hell&#xff09;是指在異步編程中&#xff0c;特別是在嵌套的回調函數中&#xff0c;代碼變得深度嵌套、難以閱讀和維護的現象。這通常發生在處理多個異步操作時&#xff0c;每個操作都依賴于前一個操作的結果。回調地獄使代碼變得難以理解、擴展…

顯卡服務器適用于哪些場景

顯卡&#xff08;GPU&#xff09;服務器&#xff0c;簡單來說&#xff0c;GPU服務器是基于GPU的應用于視頻編解碼、深度學習、科學計算等多種場景的快速、 穩定、彈性的計算服務。那么壹基比小鑫告訴你顯卡服務器主要的用途有哪一些。 一、運行手機模擬器 顯卡服務器可支持…

力扣:62. 不同路徑(Python3)

題目&#xff1a; 一個機器人位于一個 m x n 網格的左上角 &#xff08;起始點在下圖中標記為 “Start” &#xff09;。 機器人每次只能向下或者向右移動一步。機器人試圖達到網格的右下角&#xff08;在下圖中標記為 “Finish” &#xff09;。 問總共有多少條不同的路徑&…

WebRTC音視頻通話-WebRTC本地視頻通話使用ossrs服務搭建

iOS開發-ossrs服務WebRTC本地視頻通話服務搭建 之前開發中使用到了ossrs&#xff0c;這里記錄一下ossrs支持的WebRTC本地服務搭建。 一、ossrs是什么&#xff1f; ossrs是什么呢&#xff1f; SRS(Simple Realtime Server)是一個簡單高效的實時視頻服務器&#xff0c;支持RTM…

STM32CubeIDE的安裝和黑色主題及自動補全代碼

STM32CubeIDE之前用過一點時間&#xff0c;但后來因為不習慣放棄了最近在新電腦上又用起來了&#xff0c;感覺相對之前好了很多&#xff0c;其實如果在工作中基本使用的是STM32,用意法的生態軟件也挺好的&#xff0c;意法最近在這塊也在大力發展&#xff0c;STM32CubeIDE安裝包…

【BASH】回顧與知識點梳理(十三)

【BASH】回顧與知識點梳理 十三 十三. 文件內容查閱13.1 直接檢視文件內容&#xff1a;cat, tac, nlcat (concatenate)tac (反向列示)nl (添加行號打印) 13.2 可翻頁檢視&#xff1a;more, lessmore (一頁一頁翻動)less (一頁一頁翻動) 13.3 資料擷取&#xff1a;head, tailhea…

【Linux】云服務器自動化部署VuePress博客(Jenkins)

前言 博主此前是將博客部署在 Github Pages&#xff08;基于 Github Action&#xff09;和 Vercel 上的&#xff0c;但是這兩種部署方式對于國內用戶很不友好&#xff0c;訪問速度堪憂。因此將博客遷移到自己的云服務器上&#xff0c;并且基于 Jenkins&#xff08;一款開源持續…

浪涌保護器中SPD防雷模塊的主要應用方案

浪涌保護器&#xff08;Surge Protective Device&#xff0c;SPD&#xff09;是一種用于限制瞬態過電壓和導引泄放電涌電流的非線性防護器件&#xff0c;用以保護耐壓水平低的電器或電子系統免遭雷擊及雷擊電磁脈沖或操作過電壓的損害。SPD可以將過電壓泄放到地線或限制過電壓到…

類與對象(入門)

目錄 1.前言 2.類的引入 3.類的定義 4.類的訪問限定符及封裝 4.1 訪問限定符 4.2 封裝 5.類的作用域 6.類的實例化 7. 結構體內存對齊規則 8.this指針 8.1 this指針的引出 8.2 this指針的特性 1.前言 C 是 基于面向對象 的&#xff0c; 關注 的是 對象 &#xff0c;…

【Spring】核心容器——依賴自動裝配

Spring容器根據bean所依賴的資源在容器中自動查找并注入bean的過程叫做自動裝配自動裝配的方式 1、按類型 2、按名稱&#xff08;耦合性較高&#xff09; 3、按構造方法 自動裝配特點 1、自動裝配用于對引用類型進行依賴注入&#xff0c;不能對簡單類型進行操作 2、自動裝配的…

多元最短路(Floyd)

是一個基于動態規劃的全源最短路算法。它可以高效地求出圖上任意兩點之間的最短路 時間復雜度 O(n^3) 狀態轉移方程 f[i][j]min(f[i][j],f[i][k]f[k][j]) 核心代碼 void floyd(){for(int k1;k<n;k)for(int i1;i<n;i)for(int j1;j<n;j)s[i][j]min(s[i][j],s[i][k…

Vue前端 更具router.js 中的meta的roles實現路由衛士,實現權限判斷。

參考了之篇文章 1、我在登陸時獲取到登錄用戶的角色名roles&#xff0c;并存入sessionStorage中&#xff0c;具體是在login頁面實現&#xff0c;還是在menu頁面實現都可以。在menu頁面實現&#xff0c;可以顯得登陸快一些。 2、編寫router.js&#xff0c;注意&#xff0c;一個…

Spring 事務詳解

目錄 一、概述二、事務的特性&#xff08;ACID&#xff09;三、Spring 的事務管理3.1 編程式事務管理3.2 編程式事務管理 四、Spring 事務管理接口及其定義的屬性4.1 PlatformTransactionManager:事務管理接口4.2 TransactionDefinition:事務屬性4.3 TransactionStatus:事務狀態…

【基礎類】—前后端通信類系統性學習

一、什么是同源策略及限制 同源策略限制從一個源加載的文檔或腳本如何與來自另一個源的資源進行交互。這是一個用于隔離潛在惡意文件的關鍵的安全機制。源&#xff1a;協議、域名和端口&#xff0c; 默認端口是80 三者有一個不同&#xff0c;即源不同&#xff0c;就是跨域 ht…