Rust所有權詳解

文章目錄

    • Rust所有權
      • 所有權規則
        • 作用域
      • 內存和分配
        • 移動與克隆
        • 棧空間
        • 堆空間
      • 關于函數的所有權機制
        • 作為參數
        • 作為返回值
      • 引用與租借
      • 垂懸引用

Rust所有權

C/C++中我們對于堆內存通常需要自己手動管理,手動申請和釋放,即便有了智能指針,對于效率的影響和安全性問題也沒有完全解決

Rust為了高效的使用和管理內存,以及對安全性的考量,提出了所有權的概念以及一系列規則

所有權規則

所有權有三條核心規則

  1. Rust中的每個值都有一個隱含的變量,稱為所有者
  2. 一個值 同一時刻只能有一個所有者
  3. 當所有者離開作用域時,值會被丟棄(調用drop函數釋放資源)
fn main() {let s1 = String::from("Hello"); // s1 是 "Hello" 的所有者let s2 = s1; // s1 的所有權被轉移給 s2,s1 失效// println!("{}", s1); // ? 編譯錯誤:s1 已經失去所有權println!("{}", s2); // ? s2 仍然是 "Hello" 的所有者
} // s2 離開作用域,"Hello" 被釋放
作用域

這里的作用域和C/C++的作用域基本類似

{// 在聲明以前,變量 s 無效let s = "runoob";// 這里是變量 s 的可用范圍
}
// 變量范圍已經結束,變量 s 無效

內存和分配

Rust是靜態語言,這意味著我們無法像C++那樣運行時擴容,例如vector會在滿時進行擴容

在C++中=是賦值的意思,理解就是將一個變量的值,賦值給一個新的變量,這是一種拷貝語義(Copy)

但是在Rust中,變量和數據交互的方式主要是移動(Move)和克隆(Clone)

移動與克隆

如果你學過C++11,那你一定知道移動語義,例如移動拷貝或者移動賦值

這實際上是一種所有權的轉移,主要是避免頻繁申請和釋放堆空間

但是有一些情況,我們并不希望只是所有權的轉移,而是真的創建一個副本進行操作,這就需要使用clone()方法

fn main() {let s1 = String::from("hello");let s2 = s1.clone(); // 深拷貝println!("s1: {}, s2: {}", s1, s2); // ? 仍然可以使用 s1
}
棧空間

在棧空間內,Rust變量“移動”的方式其實就是復制,因為棧空間內基本上都是基本數據類型的,通常占用空間和復制時間不會很久,就會是直接復制,這時候兩個變量都是可以使用的

    let x = 1;let y = x;println!("{x}, {y}");

"基本數據"類型有這些:

  • 所有整數類型,例如 i32 、 u32 、 i64 等。
  • 布爾類型 bool,值為 true 或 false 。
  • 所有浮點類型,f32 和 f64。
  • 字符類型 char。
  • 僅包含以上類型數據的元組(Tuples)。
堆空間

String對象的hello存儲的位置可就不是棧空間了而是堆空間

例如

let s1 = String::from("hello");
let s2 = s1;

1.png

當執行到第二步時,s1就會把自己對hello字符串對所有權移交給s2,此時s1,就會失效

image.png

因此在移動之后,繼續使用s1會報錯

關于函數的所有權機制

作為參數

當一個變量作為參數傳遞給函數時,所有權應該怎么處理

fn main() {let s = String::from("hello");// s 被聲明有效takes_ownership(s);// s 的值被當作參數傳入函數,相當于s的所有權移交給函數了// 所以可以當作 s 已經被移動,從這里開始已經無效let x = 5;// x 被聲明有效makes_copy(x);// x 的值被當作參數傳入函數// 但 x 是基本類型,依然有效// 在這里依然可以使用 x 卻不能使用 s} // 函數結束, x 無效, 然后是 s. 但 s 已被移動, 所以不用被釋放fn takes_ownership(some_string: String) { // 一個 String 參數 some_string 傳入,有效println!("{}", some_string);
} // 函數結束, 參數 some_string 在這里釋放fn makes_copy(some_integer: i32) { // 一個 i32 參數 some_integer 傳入,有效println!("{}", some_integer);
} // 函數結束, 參數 some_integer 是基本類型, 無需釋放
作為返回值
fn main() {let s1 = gives_ownership();// gives_ownership 移動它的返回值到 s1let s2 = String::from("hello");// s2 被聲明有效let s3 = takes_and_gives_back(s2);// s2 被當作參數移動, s3 獲得返回值所有權
} // s3 無效被釋放, s2 被移動, s1 無效被釋放.fn gives_ownership() -> String {let some_string = String::from("hello");// some_string 被聲明有效return some_string;// some_string 被當作返回值移動出函數
}fn takes_and_gives_back(a_string: String) -> String { // a_string 被聲明有效a_string  // a_string 被當作返回值移出函數
}

引用與租借

這里的引用和C++中的引用是類似的,如果不了解C++的引用,也可以認為是一種指針

例如

fn main() {let s1 = String::from("hello");let s2 = &s1;println!("s1 is {}, s2 is {}", s1, s2);
}

按照原先的理解,s1內部會存一個指向"hello"的指針,s2內部其實也是一個指向"hello"的指針,但是s2是后來的,我們就認為s2是s1的一個引用,也就是別名

  • 引用可以認為是單獨的一種類型
  • 引用不會獲得值的所有權
  • 引用只能租借(Borrow)值的所有權
  • 當一個值被移動時,原先的引用會失效,必須重新租借所有權
fn main() {let s1 = String::from("hello");let mut s2 = &s1;let s3 = s1;s2 = &s3; // 重新從 s3 租借所有權println!("{}", s2);
}

一般的租借引用是不允許修改數據內容的,除非原先的數據是mut的,引用時也是mut引用,才允許修改

let mut s1 = String::from("hello");
let s2 = &mut s1;

此時s2允許修改s1的內容

可變引用和不可變引用除了權限不同以外,可變引用不允許多重引用(多個變量引用同一個值),但是不可變引用是允許的

這樣做主要是為了避免同時有多個使用者可以進行寫操作

垂懸引用

這個其實就是對應著空指針的概念(已經釋放資源的指針也算),例如

fn main() {let reference_to_nothing = dangle();
}fn dangle() -> &String {let s = String::from("hello");&s
}

s是在函數里申請的,函數結束自動釋放,但是返回了s的引用,被main函數接收到了,這里就相當于得到了一個空指針

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

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

相關文章

【在線OJ項目測試報告】

朋友們、伙計們,我們又見面了,本期來給大家帶來關于在線OJ項目的測試報告,如果看完之后對你有一定的啟發,那么請留下你的三連,祝大家心想事成! C 語 言 專 欄:C語言:從入門到精通 數…

【HFP】藍牙HFP應用層核心技術研究

免提配置文件(Hands-Free Profile, HFP)作為實現設備間音頻通信的關鍵協議,廣泛應用于車載系統、藍牙耳機等場景。本文將基于最新技術規范,深入剖析HFP應用層的功能要求、協議映射及編解碼器支持,為藍牙開發工程師提供詳盡的技術指南。 一、HFP應用層功能全景圖 HFP定義…

橫掃SQL面試——PV、UV問題

📊 橫掃SQL面試:UV/PV問題 🌟 什么是UV/PV? 在數據領域,UV(Unique Visitor,獨立訪客) 和 PV(Page View,頁面訪問量) 是最基礎也最重要的指標&…

【C++】第八節—string類(上)——詳解+代碼示例

hello,又見面了! 云邊有個稻草人-CSDN博客 C_云邊有個稻草人的博客-CSDN博客——C專欄(質量分高達97!) 菜鳥進化中。。。 目錄 一、為什么要學習string類? 1.1 C語言中的字符串 1.2 面試題(暫不做講解) …

如何判斷JVM中類和其他類是不是同一個類

如何判斷JVM中的類是否為同一個類 在Java虛擬機(JVM)中,判斷兩個類是否相同需要同時滿足以下三個條件: 1. 類全限定名必須相同 包括包名類名的完整路徑必須完全一致例如:java.lang.String和com.example.String被視為不同類 2. 加載該類的…

ifconfig 使用詳解

目錄 一、基本語法二、常見用途及示例1. 查看所有網絡接口信息2. 啟用/禁用網絡接口3. 配置 IP 地址和子網掩碼4. 修改 MAC 地址5. 啟用混雜模式(Promiscuous Mode)6. 設置 MTU(最大傳輸單元) 三、其他選項四、常見問題1. 新系統中…

1. 標準庫的強依賴(核心原因)

1. 標準庫的強依賴(核心原因) 容器操作(如 std::vector 擴容) 當標準庫容器(如 std::vector)需要重新分配內存時,它會嘗試移動現有元素到新內存,而非拷貝(為了性能&…

【MySQL】常用SQL--持續更新ing

一、配置信息類 1.查看版本 select version; 或 select version(); 2.查看配置 show global variables where variable_name in (basedir,binlog_format,datadir,expire_logs_days,innodb_buffer_pool_size,innodb_log_buffer_size,innodb_log_file_size,innodb_log_files_i…

Day82 | 靈神 | 快慢指針 重排鏈表

Day82 | 靈神 | 快慢指針 重排鏈表 143.重排鏈表 143. 重排鏈表 - 力扣(LeetCode) 思路: 筆者直接給跪了,這個難度真是mid嗎 直接去看靈神的視頻 環形鏈表II【基礎算法精講 07】_嗶哩嗶哩_bilibili 1.簡單來說就是&#xf…

常見的微信個人號二次開發功能

一、常見開發功能 1. 好友管理 好友列表維護 添加/刪除好友 修改好友信息(備注、標簽等) 分組管理 創建/編輯/刪除標簽 好友分類與篩選 2. 消息管理 信息發送 支持多類型內容:文本、圖片、視頻、文件、小程序、名片、URL鏈接等 附加功…

Android打包及上架應用市場問題處理

一、Gradle 配置參數含義: compileSdkVersion: 29 表示項目編譯時使用的 Android SDK 版本為 API 29(Android 10),僅影響編譯階段的行為(如代碼語法檢查、資源處理等),不直接影響運行時兼容性。…

Docker 從入門到進階 (Win 環境) + Docker 常用命令

目錄 引言 一、準備工作 1.1 系統要求 1.2 啟用虛擬化 二、安裝Docker 2.1 安裝WSL 2 2.2 安裝Docker Desktop 2.3檢查是否安裝成功 三、配置Docker 3.1 打開Docker配置中心 四、下載和管理Docker鏡像 4.1 拉取鏡像 4.2 查看已下載的鏡像 4.3 運行容器 4.4 查看正…

計算機視覺5——運動估計和光流估計

一、運動估計 (一)運動場(Motion Field) 定義與物理意義 運動場是三維場景中物體或相機運動在二維圖像平面上的投影,表現為圖像中每個像素點的運動速度矢量。其本質是場景點三維運動(平移、旋轉、縮放等&a…

介質訪問控制——信道劃分

什么是介質訪問 介質訪問(Medium Access)? 是計算機網絡中一種規則,用來解決 ??“多臺設備如何共享同一根網線/信道傳輸數據”? 的問題。你可以理解為: 想象一條只能容一輛車通過的獨木橋(網絡中的網線、Wi-Fi信道…

ERP系統五大生產模式概述

制造業中,選擇合適的生產模式是企業高效運營的關鍵。 以下是ERP系統支持的五大核心生產模式及其特點總結: 1. MTS(按庫存生產) - 定義:先生產后銷售,基于需求預測提前備貨。 - 適用場景:需求穩定、標準化程度高的產品(如日用品、家電)。 - 優點:交貨快、生產…

ubantu操作筆記

安裝ssh服務 1.1 基本安裝 sudo apt update sudo apt install openssh-server -y sudo systemctl start ssh sudo systemctl enable ssh 1.2 配置遠程root登陸 # 0. 設置root密碼 sudo passwd root # 1. 安裝vim依賴 sudo apt-get install vim -y # 2. 編輯配置文件 s…

2-vim編輯器的安裝和使用

一.常用工具介紹 前言: 我們想要編寫c語言代碼,可以使用linux系統提供的工具才能進行代碼的編輯。代碼編寫后,我們還需要驗證代碼的書寫正確。這就需要借助編譯器來進行驗證。linux系統為我們提供了比較好的開發工具。 vim編輯器&#xff…

小剛說C語言刷題——第16講 switch語句

在日常生活中,我們經常會遇到多分支的情況。當分支較多時,我們可以用嵌套的if-else語句。但是這樣會讓結構顯得混亂。這個時候我們可以考慮用switch語句。 1.語法格式 switch (表達式) { case 常量表達式1: 語句1; break; case 常量表達式…

使用 Python 連接 PostgreSQL 數據庫,從 `mimic - III` 數據庫中篩選數據并導出特定的數據圖表

要使用 Python 連接 PostgreSQL 數據庫,從 mimic - III 數據庫中篩選數據并導出特定的數據圖表,你可以按照以下步驟操作: 安裝所需的庫:psycopg2 用于連接 PostgreSQL 數據庫,pandas 用于數據處理,matplot…

過孔的載流能力

PCB過孔的載流能力(即能安全承載的電流大小)主要與以下因素相關: 1. 過孔的尺寸 孔徑(直徑):孔徑越大,橫截面積越大,載流能力越強。 孔壁銅厚:電鍍銅的厚度&#xff08…