Rust 變量與可變性

文章目錄

  • 變量與可變性
    • 常量
    • 遮蔽(Shadowing)

變量與可變性

Rust 變量與可變性

Rust中變量默認是不可變的,這是 Rust 鼓勵你編寫更安全、易于并發代碼的眾多方式之一。不過,你仍然可以選擇讓變量可變。讓我們來探討 Rust 為什么鼓勵你優先使用不可變性,以及為什么有時你可能需要選擇可變性。

當變量是不可變的時,一旦一個值被綁定到名字上,你就無法更改該值。為說明這一點,請在你的項目目錄下用 cargo new variables 創建一個新項目。

然后,在新建的 variables 目錄下,打開 src/main.rs,并將其代碼替換為以下內容(此代碼暫時無法編譯):

文件名:src/main.rs

此代碼編譯不通過!

fn main() {let x = 5;println!("The value of x is: {x}");x = 6;println!("The value of x is: {x}");
}

保存并使用 cargo run 運行程序。你會收到關于不可變性錯誤的提示,如下所示:

$ cargo runCompiling variables v0.1.0 (file:///projects/variables)
error[E0384]: cannot assign twice to immutable variable `x`--> src/main.rs:4:5|
2 |     let x = 5;|         - first assignment to `x`
3 |     println!("The value of x is: {x}");
4 |     x = 6;|     ^^^^^ cannot assign twice to immutable variable|
help: consider making this binding mutable|
2 |     let mut x = 5;|         +++

有關此錯誤的更多信息,請嘗試 rustc --explain E0384
error: could not compile variables (bin “variables”) due to 1 previous error

這個例子展示了編譯器如何幫助你發現程序中的錯誤。

你收到“不能對不可變變量 x 進行二次賦值”的錯誤,是因為你試圖給不可變變量 x 賦第二個值。

當我們試圖更改被指定為不可變的值時,在編譯時報錯是很有價值的,因為不應改變值的變量發生了改變很容易導致 bug。如果代碼的一部分要求某個值永遠不變,而另一部分代碼卻改變了這個值,那么第一部分代碼可能就無法按預期工作。尤其是當第二部分代碼只在某些情況下才改變值時,這種 bug 很難追蹤。Rust 編譯器保證你聲明值不會改變時,它真的不會改變,因此你無需自己跟蹤。這樣你的代碼更易于理解。

但可變性有時非常有用,也能讓代碼更方便編寫。雖然變量默認是不可變的,但我們可以在變量名前加上 mut 使其可變。加上 mut 也向未來的代碼閱讀者傳達了意圖,表明代碼的其他部分會更改該變量的值。

例如,讓我們將 src/main.rs 改為如下內容:

文件名:src/main.rs

fn main() {let mut x = 5;println!("The value of x is: {x}");x = 6;println!("The value of x is: {x}");
}

現在運行程序,輸出如下:

$ cargo runCompiling variables v0.1.0 (file:///projects/variables)Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.30sRunning `target/debug/variables`
The value of x is: 5
The value of x is: 6

使用 mut 后,我們可以將 x 的值從 5 改為 6。最終,是否使用可變性取決于我們實際的需求和應用場景。

常量

與不可變變量類似,常量也是綁定到名字上的值,且不允許更改,但常量和變量之間有一些區別。

首先,常量不能使用 mut。常量不僅默認不可變——它們始終不可變。我們需要用 const 關鍵字聲明常量,而不是 let,并且必須標注值的類型。

常量可以在任何作用域聲明,包括全局作用域,從而可供多個模塊或代碼片使用。

最后一個區別是,常量只能被賦值為常量表達式,而非在運行時計算的值。

下面是一個常量聲明的例子:

const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;

常量名為 THREE_HOURS_IN_SECONDS,其值為 60(每分鐘的秒數)乘以 60(每小時的分鐘數)再乘以 3(我們想要計算的小時數)。Rust 的常量命名約定是全大寫并用下劃線分隔單詞。編譯器能在編譯時完成有限的運算,這讓我們可以用更易理解的方式寫出這個值,而不是直接寫 10,800。更多關于常量聲明時可用操作的信息,請參閱 Rust Reference 的常量求值部分。

在聲明的作用域內,常量于整個程序運行期間都有效。此屬性使得常量可以作為多處代碼使用的全局范圍的值,例如一個游戲中所有玩家可以獲取的最高分或者光速。

將程序中用到的硬編碼值聲明為常量,能幫助后來的代碼維護人員了解值的意圖。如果將來需要更改硬編碼值,也只需改動一處即可。

遮蔽(Shadowing)

正如你在第 2 章的猜數字游戲教程中看到的,你可以用相同的名字聲明新變量。Rustacean 稱第一個變量被第二個變量“遮蔽”,即編譯器在使用變量名時會看到第二個變量。實際上,第二個變量覆蓋了第一個變量,直到它自己被遮蔽或作用域結束。我們可以通過重復使用 let 關鍵字和相同變量名來遮蔽變量,如下所示:

文件名:src/main.rs

fn main() {let x = 5;let x = x + 1;{let x = x * 2;println!("The value of x in the inner scope is: {x}");}println!("The value of x is: {x}");
}

該程序首先將 x 綁定為 5。然后通過 let x = x + 1; 創建了一個新變量 x,此時 x 的值為 6。接著,在用花括號創建的內部作用域中,第三個 let 語句再次遮蔽 x,創建了一個新變量,其值為前一個值乘以 2,即 12。當該作用域結束后,內部遮蔽結束,x 恢復為 6。運行該程序,輸出如下:

$ cargo runCompiling variables v0.1.0 (file:///projects/variables)Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.31sRunning `target/debug/variables`
The value of x in the inner scope is: 12
The value of x is: 6

遮蔽與將變量標記為 mut 不同,因為如果你試圖在沒有使用 let 關鍵字的情況下重新賦值,會得到編譯時錯誤。通過使用 let,我們可以對值進行多次轉換,但在這些轉換完成后變量仍然是不可變的。

mut遮蔽的另一個區別是,使用 let 關鍵字實際上創建了一個新變量,因此我們可以更改值的類型,但仍然復用相同的名字。例如,假設我們的程序讓用戶輸入文本之間要有多少個空格,用戶輸入空格字符后,我們想把這個輸入存儲為數字:

let spaces = "   ";
let spaces = spaces.len();

第一個 spaces 變量是字符串類型,第二個 spaces 變量是數字類型。遮蔽讓我們無需起不同的名字(如 spaces_str 和 spaces_num),而可以復用更簡單的 spaces 名字。但如果我們嘗試用 mut,如下所示,會得到編譯時錯誤:

此代碼無法編譯!

let mut spaces = "   ";
spaces = spaces.len();

錯誤提示我們不能更改變量的類型:

$ cargo runCompiling variables v0.1.0 (file:///projects/variables)
error[E0308]: mismatched types--> src/main.rs:3:14|
2 |     let mut spaces = "   ";|                      ----- expected due to this value
3 |     spaces = spaces.len();|              ^^^^^^^^^^^^ expected `&str`, found `usize`

有關此錯誤的更多信息,請嘗試 rustc --explain E0308
error: could not compile variables (bin “variables”) due to 1 previous error

整體代碼示例如下:

fn main() {// 變量可變性  let mut x = 5;println!("The value of x is: {x}");x = 6;println!("The value of x is: {x}");// 常量const MAX_POINTS: u32 = 100_000;println!("The maximum points is: {MAX_POINTS}");// 變量遮蔽let x = 5;let x = x + 1;{let x = x * 2;println!("The value of x in the inner scope is: {x}");}println!("The value of x in the outer scope is: {x}");
}

運行結果如下

The value of x is: 5
The value of x is: 6
The maximum points is: 100000
The value of x in the inner scope is: 12
The value of x in the outer scope is: 6

現在我們已經了解了變量的工作方式,接下來讓我們看看它們可以擁有的數據類型。

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

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

相關文章

sourcetree無法獲取遠程所有的tag

2025年5月29日11:30:17 sourcetree widnwos v3.4.23版本 突然發現線上的代碼庫里有很多新打的tag,但是sourcetree死活無法拉去所有的tag,嘗試卸載重新安裝也不行,全網也找了還是不知道,但是mac版本好像沒有這個問題 方法1&…

《深度探索C++對象模型》閱讀筆記(完整版)

《深度探索C對象模型》閱讀筆記(完整版) 文章目錄 《深度探索C對象模型》閱讀筆記(完整版)1. 關于對象(Object Lessons)1.1 C對象模型(The C Object Model)1.1.1 語言中的對象模型1.…

從Docker拉取鏡像一直失敗超時解決辦法

項目場景: 在ubuntu中,使用docker拉去鏡像時,一直超時,拉去失敗。 問題描述 原因分析: 國外服務器網絡不好導致。 解決方案: 解決方案1 設置國內源 我這邊測試,更改以后仍然失敗 阿里云提供…

KONG根據請求參數限流

背景 價格接口 /search 同時支持緩存查價和實時查價,主要通過searchType字段區分這兩種請求。 searchType 為空時為緩存查價,QPS很高。searchType 不為空時為實時查價,但QPS遠低于普通查價。 如果直接對該接口限流,當流量波動超…

通俗易懂解析:@ComponentScan 與 @MapperScan 的異同與用法

在 Spring 和 MyBatis 集成開發中,ComponentScan 和 MapperScan 是兩個核心注解,但它們的用途和工作機制截然不同。本文將通過通俗的語言和示例代碼,帶您輕松掌握它們的區別和使用方法。 一、基礎概念 ComponentScan:Spring 的“通…

39. 自動化異步測試開發之編寫異步業務函數、測試函數和測試類(函數寫法)

39. 自動化異步測試開發之編寫異步業務函數、測試函數和測試類(函數寫法) 一、異步業務函數解析 1.1 頁面導航函數 async def get(async_driver, url: str http://secure.smartbearsoftware.com/samples/testcomplete12/WebOrders/Login.aspx):await…

Qt 無邊框窗口實現拖動與窗口控制(最小化/最大化/關閉)

在 Qt 中,使用 Qt::FramelessWindowHint 可以創建無邊框窗口,但這樣會導致窗口無法拖動,并且系統默認的標題欄按鈕(最小化、最大化、關閉)也會消失。本文將介紹如何實現無邊框窗口的鼠標拖動功能,并添加自定…

Linux中的System V通信標準-共享內存、消息隊列以及信號量

在Linux系統中,System V IPC(Inter-Process Communication)提供了一系列進程間通信的機制,包括共享內存、消息隊列和信號量。這些機制在系統中發揮了重要作用,幫助進程之間進行數據交換和同步。本文將詳細介紹這些機制…

postman工具使用

基本功能操作 常用斷言 定義:postman 斷言借助 JavaScript - js 語言編寫代碼,自動判斷預期結果與實際結果是否一致。( 注意斷言 代碼寫在 Tests 的標簽中) 斷言響應狀態碼 斷言響應體是否包含某個字符串(Response bo…

VBA數據庫解決方案二十:Select表達式From區域Where條件Order by

《VBA數據庫解決方案》教程(版權10090845)是我推出的第二套教程,目前已經是第二版修訂了。這套教程定位于中級,是學完字典后的另一個專題講解。數據庫是數據處理的利器,教程中詳細介紹了利用ADO連接ACCDB和EXCEL的方法…

算法-集合的使用

1、set常用操作 set<int> q; //以int型為例 默認按鍵值升序 set<int,greater<int>> p; //降序排列 int x; q.insert(x); //將x插入q中 q.erase(x); //刪除q中的x元素,返回0或1,0表示set中不存在x q.clear(); //清空q q.empty(); //判斷q是否為空&a…

C++文件和流基礎

C文件和流基礎 1. C文件和流基礎1.1 文件和流的概念1.2 標準庫支持1.3 常用文件流類ifstream 類ofstream 類fstream 類 2.1 打開文件使用構造函數打開文件使用 open() 成員函數打開文件打開文件的模式標志 2.2 關閉文件使用 close() 成員函數關閉文件關閉文件的重要性 3.1 寫入…

Maven---配置本地倉庫

目錄 5. 5.1在Maven路徑下新建文件夾用于本地倉庫存儲 5.2 復制本地倉庫路徑 5.3 找到配置文件路徑&#xff0c;使用VSCode方式打開 5.4 新增一行代碼 5.5 復制本地倉庫路徑&#xff0c;設置存儲路徑 5.1在Maven路徑下新建文件夾用于本地倉庫存儲 5.2 復制本地倉庫路徑 5…

Vue3 + Element Plus + TypeScript 中 el-cascader 實現模擬用戶點擊功能

模擬點擊&#xff0c;調用 el-cascader 的公開方法 togglePopperVisible 來展開下拉框 MaterialOut.vue <script setup lang"ts" name"MaterialOut"> ...... import { ElMessage, type ElCascader } from "element-plus";// 級聯組件實例…

新能源汽車與油車銷量

中國油車與新能源車銷量對比&#xff08;2022-2025年&#xff09; ?1. 市場份額演化&#xff08;2022-2025年&#xff09;? ?年份? ?新能源車銷量 &#xff08;滲透率&#xff09;? ?燃油車銷量 &#xff08;滲透率&#xff09;? ?關鍵事件? ?2022? 688.7萬輛…

C++ list代碼練習、set基礎概念、set對象創建、set大小操作

對應力扣&#xff0c;回文鏈表&#xff0c;代碼見下 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(int x, …

前端面試寶典---前端水印

明水印 1. 背景圖 通過css的background-image加載背景圖 2. canvasbackground水印 前端水印實現思路與示例代碼 一、核心實現思路 Canvas動態生成水印 通過Canvas繪制文本或圖案&#xff0c;將生成的圖像轉為Base64格式&#xff0c;作為背景圖重復平鋪到目標元素上。例如&…

惡意軟件清理工具,讓Mac電腦安全更簡單

?你的Mac最近是不是開始表演"電子迷惑行為"&#xff1f;瀏覽器主頁突然變成澳門賭場&#xff0c;風扇轉得比直升機螺旋槳還猛......恭喜你&#xff01;可能中獎獲得"惡意軟件大禮包"&#xff01;別慌&#xff0c;今天就教你用惡意軟件清理工具化身數字特工…

Spring Boot 3.X 下Redis緩存的嘗試(二):自動注解實現自動化緩存操作

前言 上文我們做了在Spring Boot下對Redis的基本操作&#xff0c;如果頻繁對Redis進行操作而寫對應的方法顯示使用注釋更會更高效&#xff1b; 比如&#xff1a; 依之前操作對一個業務進行定入緩存需要把數據拉取到后再定入&#xff1b; 而今天我們可以通過注釋的方式不需要額外…

Deepseek應用技巧-Dify安裝和踩坑指南

前言&#xff1a;Dify的名號是非常大的&#xff0c;作為私有化AI部署中必不可少的一個組件&#xff0c;他的功能和COZE十分相似&#xff0c;可以進行工作流和智能體的搭建&#xff0c;有非常強大的功能&#xff0c;那本節就將來揭開Dify的神秘的面紗&#xff0c;首先看一下Dify…