Rust語言入門教程(二) - 變量與作用域

變量與作用域

變量的聲明與初始化

Rust的基本語法格式如下:

fn main(){let bunnies = 2;
}

語句以分號結尾,用花括號包含語句塊。 Rust的語法其實借鑒了很多其他的語言,比如C語言和Python, 所以變量定義的格式看起來也跟很多我們熟悉的其他語言相似。Rust中,使用let關鍵字聲明一個變量。在上面的例子中, 我們聲明了一個變量bunnies, 并且初始化了它的值為2.

Rust是一種強類型的語言,那么在上面的語句中,哪里標注了這個變量的類型呢?在Rust編程中,如果Rust能準確的識別這個變量的類型,那么我們不需要顯式的標注變量的類型,也不需要像C#那樣標注一個auto表示它的類型是自動識別的。

如果需要顯式的標注一個變量的類型,可以像下面的例子一樣做, 在變量名后加個: , 后面再寫上變量的類型,如下,i32代表有符號的32位整型。

fn main(){let bunnies: i32 = 2;
}

與python類似,rust也可以在一行語句中定義多個變量。如下例子便可以在一行代碼中為兩個變量初始化:

fn main(){let (bunnies, carrots) = (8, 50);
}

變量不可變

在Rust中,變量默認其實是不可變的,也就是說,一旦對一個變量賦值以后,其值默認是不可被修改的。這一特點與大多數的其他編程語言都不同,其他編程語言的變量默認是隨時可以被重新賦值的。那為什么Rust要將變量設置為默認不可變的呢?這就要提到上一章中我們提到的Rust的三個特性了:

  • 內存安全: 如果變量在運行過程中始終不變,這可以避免很多bug的發生,變量不可變這一設計,極大的提高了Rust的內存安全特性。
  • 無畏并發: 不變的變量,可以被多個線程在不加鎖的情況下共享,這也使得Rust的并發更安全可靠。
  • 高性能: 不變的變量,使得編譯器可以對其進行額外的優化,從而提高了代碼的執行速度,提高了程序運行性能。

但是不得不承認我們在編程中一定會遇到需要修改變量的需求, 如果我們直接修改變量的值,編譯便會報錯,例如下面的代碼:

fn main(){let bunnies: i32 = 2;bunnies = 3;   // Error!
}

如果運行上面的代碼,將會得到下面的報錯,可以看到,報錯中非常明確的指出了代碼的問題所在,并且還指出了修改建議, 在報錯的最上面,給出了錯誤的描述,也就是:不能對不可變變量進行二次賦值。在報錯中,也指出了錯誤所在的位置,第3行第5列。接下來還對整個錯誤的上下文進行了說明,告訴我們在第2行的時候對變量bunnies已經賦值,然后再第3行再次對不可變變量bunnies進行了賦值,因此報錯。接著還提出了修改建議,讓我們在第2行的變量名前面加上mut, 使其成為一個可變變量,也許能修復這個問題。在最后一行,如果上面的提示還不能解決問題,還可以運行rustc --explain e0384來查看錯誤的完整描述。
請添加圖片描述
按照錯誤提示,我們將代碼修改后如下便可以成功運行了:

fn main(){let mut bunnies: i32 = 2;bunnies = 3;   // Error!
}

常量

在Rust中,常量(constant)其實也屬于變量的一種, 相比普通的不可變變量,它更加的不可變。定義一個常量包含以下四個關鍵步驟:

  • const而不是let聲明;
  • 變量名格式為全大寫字母加下劃線分隔;
  • 必須聲明變量類型;
  • 常量的值必須時編譯時可確定值的表達式;

下面是普通變量和常量聲明的對比:

let wrap_factor = ask_scotty();  // 變量
const WRAP_FACTOR: f64 = 9.9;    // 常量

定義一個常量比變量麻煩很多,那為什么還要用常量呢?

  • 常量可以在函數作用域外或者模塊外進行定義,而在任意的地方使用;
  • 常量會在編譯時被靜態的寫入可執行文件,使得運行速度很快;
  • Rust官方在每個發布版本中都對const類型增加了越來越多的功能和優化,在可以使用const的地方,使用const是一個好的選擇;

作用域

每個變量都有各自的作用域,只有在變量的作用域中,變量才能被使用。代碼的作用域通常是從變量被創建的地方開始,到變量所在的代碼塊結束, 在這個范圍中的子代碼塊中,變量仍然是可以被訪問的。

注: 代碼塊是一組被花括號包含的語句

fn main() {let x = 5;{let y = 99;println!("x = {}, y = {}", x, y);}println!("x = {}, y = {}", x, y); // Error!
}

在上面的代碼中, 變量xmain函數的代碼塊中被定義,其中定義了一個子代碼塊,在子代碼塊中定義了一個變量y, 在子代碼塊中,xy都可以被訪問, 在子代碼塊結束時, y立刻被銷毀(Rust中沒有任何的垃圾回收器,變量總是在離開作用域后被立即銷毀),因此第二個println!語句不能訪問變量y而發生錯誤。

然而我們不用擔心這會在運行時發生bug, 因為這種錯誤會在編譯時就被暴露出來。
請添加圖片描述

變量隱藏

Rust中,也存在變量隱藏的現象

fn main() {let x = 5;{let x = 99;println!("x = {}", x);}println!("x = {}", x); // Error!
}

運行結果應該如下:

x = 99
x = 5

在上述代碼中我們在子代碼塊外部定義了一個變量x并賦值為5, 在子代碼塊中,x的值被覆蓋,為內層代碼塊中的值99。當離開了內層代碼塊后,內層的變量x被銷毀, x的值又變回了外層代碼塊中的5.

再來看一個例子:

fn main() {let mut x = 5; // x is mutablelet x = x;     // x is now immutable
}

這個例子中,第一個x被隱藏了,這其實相當于重新聲明并初始化了x這個變量,在編譯過程中, Rust甚至能識別到這種情形并優化執行的過程,并不會真的先定義一個可變的x, 再用一個新的x去覆蓋它,而是直接定義一個不可變的變量x并為其賦值為5.

再看一個例子:

fn main() {let meme = "More cowbell!";let meme = make_image(meme);
}

在上述代碼中, 變量meme甚至能被改變類型(從字符串變成了圖片)。

變量與內存安全

在Rust中,在使用一個變量前,必須確保這個變量被初始化。

情景A

fn main() {let enigma: i32;println!("{}", enigma);  // Error!
}

請添加圖片描述
可以看到,報錯提示我們,變量雖然被聲明了,但是沒有被初始化。

情景B

fn main() {let enigma: i32;if true{enigma = 42;}println!("{}", enigma);  // Error!
}

請添加圖片描述
即時是在一個恒為真的判斷語句中為變量進行了初始化,編譯器仍會報錯, 因為判斷語句只有在運行時才能被判別最終的結果,因此在編譯時沒辦法確保該變量一定會被初始化。

為了保證變量一定被初始化,可以將上述代碼改為如下:

fn main() {let enigma: i32;if true{enigma = 42;} else {enigma = 7;}println!("{}", enigma);  // Error!
}

如果在C語言中使用了一個未初始化的變量會出現什么現象呢,如下代碼:

include <stdio.h>
int main(){int enigma;printf("%d\n", enigma);
}

這將不會導致編譯報錯,程序可以正常運行,但是會輸出一個不可預測的結果,因為聲明變量后, C語言就會在內存分配一個地址,而這個內存地址中存儲的是什么數據,我們不得而知,它可能是任何東西。

小結

本章介紹了Rust中變量的種類,聲明與賦值方式,以及變量的作用域和隱藏特性。下一章將介紹Rust的函數及模塊系統。

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

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

相關文章

shell 腳本的函數和數組

函數 —— 封裝的一個公式&#xff1a;sin、cos、tan —— 函數為腳本的別名 —— 函數就是一個功能模塊&#xff0c;在函數中寫執行的命令即可&#xff1b;使用函數可以避免代碼重復&#xff0c;增加可讀性&#xff0c;簡化腳本&#xff0c;使用函數可以將大的工程分割為若…

Vue中動態class方法

1.方法一&#xff1a;只需要展示一個class樣式&#xff0c;但是動態取值 <div :class"aaa"></div>data:{return () {aaa:colorRed} }.colorRed {color: red; } 2.方法二&#xff1a;表達式判斷是否展示class里面的樣式 <div :class"{colorRed…

函數與數組

一.函數 1、函數的作用 定義較為復雜的但是需要重復使用的內容&#xff0c;以便再次使用&#xff0c;可以直接調用&#xff0c;節約時間&#xff0c;提高效率。 語句塊定義成函數約等于別名&#xff0c;定義函數&#xff0c;再引用函數。 封裝的可重復利用的具有特定功能的…

python+django高校科研項目管理系統2u3mx

高校科研項目管理系統采用擬開發的高校科研項目管理系統通過測試,確保在最大負載的情況下穩定運轉,各個模塊工作正常,具有較高的可用性。系統整體界面簡潔美觀,用戶使用簡單,滿足用戶需要。在因特網發展迅猛的當今社會,高校科研項目管理系統必然會成為在數字信息化建設的一個重…

Linux 查看 CPU 信息相關命令

目錄 lscpu top htop lscpu lscpu 命令是用于顯示有關 CPU 架構的信息&#xff0c;而不是用于實時監視 CPU 負載。它提供有關 CPU 類型、核心數、線程數、架構等的詳細信息。 lscpu top 命令會顯示系統的實時狀態&#xff0c;包括 CPU 使用率、內存使用率等。按下鍵盤上的…

一文詳解Vue生命周期

Vue是一種流行的用于構建用戶界面的漸進式JavaScript框架。Vue框架在開發過程中&#xff0c;特別強調對生命周期的理解和管理。通過使用生命周期鉤子函數&#xff0c;開發者能夠精確地控制Vue實例的創建、掛載、更新以及銷毀過程。本文將對Vue的生命周期進行詳細的介紹&#xf…

YOLOv8訓練自己的目標檢測數據集

YOLOv8訓練自己的目標檢測數據集 目錄標題 源碼下載環境配置安裝包訓練自己的數據集數據集文件格式數據集文件配置超參數文件配置訓練數據集命令行訓練腳本.py文件訓練 進行detect顯示detect的效果 源碼下載 YOLOv8官方的GitHub代碼&#xff0c;同時上面也有基礎環境的配置要…

IntelliJ IDEA 16創建Web項目

首先要理解一個概念&#xff1a;在IntelliJ IDEA中“new Project”相當于eclipse中的工作空間&#xff08;Workspace&#xff09;&#xff0c;而“new Module”相當于eclipse中的工程&#xff08;Project&#xff09;。以下均采用Intellij的說法&#xff0c;請自行對照轉換理解…

Maven倉庫清除remote.repositories、lastUpdated、sha1腳本

Maven倉庫清除remote.repositories、lastUpdated、sha1腳本 1. 腳本功能 清除本地maven倉庫的_remote.repositories、sha1、lastUpdated的文件 2. 腳本內容 set REPOSITORY_PATH%cd% rem 搜索中... for /f "delims" %%i in (dir /b /s "%REPOSITORY_PATH%\*…

Java 關鍵字:synchronized詳解

synchronized詳解 基本使用源碼解析常見面試題好書推薦 基本使用 Java中的synchronized關鍵字用于在多線程環境下確保數據同步。它可以用來修飾方法和代碼塊 當一個線程訪問一個對象的synchronized方法或代碼塊時&#xff0c;其他線程將無法訪問該對象的其他synchronized方法或…

【愚公系列】2023年11月 Java教學課程 188-SpringCloud(Feign遠程調用)

&#x1f3c6; 作者簡介&#xff0c;愚公搬代碼 &#x1f3c6;《頭銜》&#xff1a;華為云特約編輯&#xff0c;華為云云享專家&#xff0c;華為開發者專家&#xff0c;華為產品云測專家&#xff0c;CSDN博客專家&#xff0c;阿里云專家博主&#xff0c;阿里云簽約作者&#xf…

如何通過寶塔面板搭建一個本地MySQL數據庫服務并實現遠程訪問

寶塔安裝MySQL數據庫&#xff0c;并內網穿透實現公網遠程訪問 文章目錄 寶塔安裝MySQL數據庫&#xff0c;并內網穿透實現公網遠程訪問前言1.Mysql服務安裝2.創建數據庫3.安裝cpolar3.2 創建HTTP隧道 4.遠程連接5.固定TCP地址5.1 保留一個固定的公網TCP端口地址5.2 配置固定公網…

浙江師范黃昌勤老師關于情感分析的講座

回放&#xff1a;https://share.weiyun.com/Wvaw4OoY 收獲很大&#xff1a; 寫幾點&#xff0c; 1&#xff1a;小二聚焦 精細更有用 2&#xff1a;高產的秘訣就是 專注一個領域&#xff0c;不斷延展。 3&#xff1a;好的文章技術不是本質&#xff0c;本質還是解決了什么教育問…

九、ffmpeg命令轉封裝

開了幾天小差&#xff0c;今天繼續學習ffmpeg。 準備測試使用的視頻&#xff0c;并查看其信息 # 查看視頻信息。使用Mediainfo也可以 ffprobe test.mp4 視頻格式的信息如下。 保持編碼格式&#xff1a;ffmpeg -i test.mp4 -vcodec copy -acodec copy test_copy.tsffmpeg -i…

WordPress網站如何修復數千個帖子的SEO錯誤

在本教程中&#xff0c;我們將向您展示如何解決您經常犯的SEO錯誤。 最好的是您不必花費太多時間&#xff0c;因為您不需要打開并編輯每個帖子。 相反&#xff0c;我們將向您展示如何使用 WordPress 內的電子表格來修復 WordPress 帖子的 SEO。 在這里&#xff0c;我們為您提…

nint和Pattern matching介紹(C#)

nint 最近看C# 9.0時&#xff0c;發現一個有意思的關鍵詞&#xff0c;就是nint&#xff0c;第一次看到這個&#xff0c;于是好奇心爆棚&#xff0c;就去實際操作了一下。 nint i 1000; Console.WriteLine("i{0}", i);實際結果與int的結果是一樣的&#xff0c;那為什…

ARM裸機-19(NandFlash和iNand)

1、NandFlash的接口 1.1、Nand的型號與命名 (1)、Nand的型號命名都有含義&#xff0c;就拿K9F2G08來示例分析一下&#xff1a;K9F表示是三星公司的NandFlash系列。2G表示Nand的大小是2Gbit (256MB)。08表示Nand是8位的 (8位就是數據線有8根)。 (2)、Nand命名中可以看出&#x…

【2023.11.23】JDBC基本連接語法學習?

1.導入jar包依賴&#xff1a;mysql-connector-java-8.0.27.jar 2.連接數據庫&#xff01; 3.無法解析類->導入java.sql.*&#xff0c;&#xff08;將項目方言改為Mysql&#xff09; JDBC&#xff0c;啟動&#xff01;&#xff01; public class Main {public static voi…

如何下載OpenJDK及其源碼

如果想下載 OpenJDK&#xff0c;存在以下幾種辦法&#xff1a; 最簡單的辦法是去 OpenJDK 官網&#xff0c;這里能下載 JDK9 及其以上的版本&#xff0c;還有 JDK 源碼所在的 github 地址。 第二種方法是使用 IDEA 下載&#xff0c;位置在 File->Project Structure->SD…

【Vue】核心特性(響應式)

響應式&#xff1a; 數據變化&#xff0c;視圖自動更新 接下來使用一個例子來體現一下什么是響應式 案例一&#xff1a; 訪問數據&#xff0c;視圖自動更新 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><…