Rust入坑指南:常規套路
搭建好了開發環境之後,就算是正式跳進Rust的坑了,今天我就要開始繼續向下挖了。
由於我們初來乍到 ,對Rust還不熟悉,所以我決定先走一遍常規套路。
變不變的變數
學習一門語言第一個要了解的當然就是變數啦。Rust使用關鍵字let
來定義變數。我們寫一個簡單的demo
so easy!等等,這個小紅線是怎麼回事?編譯錯誤???彆著急,哪裡不對點哪裡。
IDEA告訴我,這個錯誤是
Cannot assign twice to immutable variable [E0384]
複製程式碼
不可變的變數不能賦值兩次。我定義的變數是不可變的?這能叫變數?
官方檔案對此的解釋是,對於一個變數,你在一部分程式碼中不希望它改變,而在另一部分程式碼中修改了它。那麼第一部分程式碼的執行結果也許就不符合預期了。所以Rust的編譯器為了杜絕這種情況,也就是說Rust的變數預設是不可修改的。也就是說你只能對它賦值一次,之後它就是隻讀的了。有點像Java中的final變數。嗯…很嚴格。
但我們程式設計中還是需要變數的,怎麼辦?下面是跟著IDEA學習寫程式碼環節。直接使用Alt + Enter,IDEA會在定義x時加上mut
關鍵字。
果然不會報錯了。感謝IDEA。接下來執行試試
The value of x is: 5
The value of x is: 6
複製程式碼
列印結果符合我們的預期。
mut
關鍵字在官方檔案也有解釋,定義變數時加上mut
,表明這個變數是一個可變變數。Rust之所以設計了可變變數,還有一個比較重要的原因就是,對於比較複雜的資料型別,每次修改都要複製並且分配新的記憶體。這種效能損耗有時候是無法接受的。因此可以選擇使用可變變數來提高效能。
變數和常量
Rust本身也支援常量,可能大多數同學和我有一樣的疑問,常量和不可變變數有什麼區別呢?
事實上它們還是有區別的。最明顯的就是名字不一樣。(這是一句廢話)
主要的區別有以下幾種:
- 定義常量時不能使用
mut
關鍵字 - 常量定義使用的關鍵字是
const
,並且需要指定資料型別。定義變數使用的是let
- 常量可以在任何範圍內定義,並且可以在多個程式碼塊中使用
- 給常量賦值時,不能使用函式的返回值或者是計算式。只能使用一個「常量」
變數的覆蓋
Rust是一門靜態程式語言,對於大多數靜態程式語言中,在同一範圍內,變數名是不允許重複的。但是Rust允許這樣定義。類似於這樣:
fn main() {
let x = 5;
let x = x + 1;
let x = x + 2;
println!("The value of x is: {}",x);
}
複製程式碼
這讓人看起來有些疑惑,作為一個Java程式設計師,在我看來第二行就應該報編譯錯誤了。但我們剛提到了Rust是允許這樣定義的。對於上述程式碼來講,每次定義x都會覆蓋前一次定義的x。
對於Java來講,將一個int型別的變數轉換成String型別的變數可能需要這樣做:
int codeInt = 1;
String codeStr = String.valueOf(codeInt);
複製程式碼
我們需要定義兩個變數來分別接收不同型別的變數,為了變數名更有意義,可能要在變數名中加上變數型別。而在Rust中就不用考慮這個問題。
let s = "123";
let s: u32 = s.parse().expect("Not a number!");
複製程式碼
這樣定義之後,再使用變數s時,它都是u32型別的變量了。
上面這個例子就是覆蓋變數和可變變數的區別:可變變數不可以修改變數型別,覆蓋變數可以做型別轉換。
資料型別
可能有些同學不太理解Rust為什麼是靜態語言。這是因為在編譯階段,Rust編譯器必須要明確每個變數的型別。編譯器通常會根據變數的值或者使用方法來為變數指定一個資料型別。如果某個變數可能的資料型別有多個,那麼就需要開發者手動指定。
像上一節的例子中,第二次定義s如果不指定型別u32,編譯就會報錯。Rust支援的資料型別都有哪些呢?
和多數程式語言一樣,Rust支援的資料型別可以分為基本資料型別和複合資料型別兩大類。先說基本資料型別,基本資料型別分為整數型、浮點型、布林型和字元型。我們逐個介紹一下。
整數型
Rust支援的整數型別分為有符號整數和無符號整數
Length | Signed | Unsigned |
---|---|---|
8-bit | i8 | u8 |
16-bit | i16 | u16 |
32-bit | i32 | u32 |
64-bit | i64 | u64 |
128-bit | i128 | u128 |
arch | isize | usize |
如果沒有指定資料型別,Rust預設使用i32
,這個型別通常是效能最好的。
再順便聊一下整數的幾種表示。
Number literals | Example |
---|---|
Decimal | 98_222 |
Hex | 0xff |
Octal | 0o77 |
Binary | 0b1111_0000 |
Byte(u8) | b'A' |
十進位制中_
一般被當作千分符。
浮點型
Rust的浮點型別不像整型那麼多,它只支援兩種:f32和f64分別表示32位和64位浮點數,預設的浮點型別是f64。
布林型別
布林型別沒有什麼特別的,Rust支援隱式和顯式宣告兩種
let t = true;
let f: bool = false;
複製程式碼
字元型
需要注意的是字元型別char使用單引號,字串使用雙引號。字元型別的值可以是Unicode標準值。範圍是從U+0000到U+D7FF和U+E000到U+10FFFF。這意味著它可以是中文韓文 emoji等等,而並不侷限於直覺上的「字元」。
聊完了基本資料型別,再來聊一聊複合型別,Rust包含兩種複合型別:Tuple和Array。
Tuple型別
Tuple是一種可以儲存不同型別的數字的集合。它的長度固定。宣告方法是:
let tup: (i32,f64,u8) = (500,6.4,1);
複製程式碼
如果想要取得tuple中的某一個值,通常有兩種方法,一種是將tuple分別賦值給多個變數
fn main() {
let tup = (500,1);
let (x,y,z) = tup;
println!("The value of y is: {}",y);
}
複製程式碼
另一種方法是用直接用「.」來取值。
fn main() {
let tup = (500,1);
let x = tup.0;
let y = tup.1;
let z = tup.2;
println!("x: {},y: {},z: {}",x,z);
}
複製程式碼
Array型別
Array也是多個值的集合,但與Tuple不同的是,Array中的各個元素的資料型別必須相同。Array的長度也是固定的,這點上Rust的Array和其他語言的有所不同。Array的定義方法是:
fn main() {
let a = [1,2,3,4,5];
}
複製程式碼
Rust的陣列儲存在棧中,而不是堆。如果你不能在定義時確定陣列的長度,那麼需要使用vector型別,這個我們在後面討論。Array還有一些其他的定義方法。
let a: [i32; 5] = [1,5];
複製程式碼
i32表示陣列中元素的型別,5表示元素數量。
如果初始化時所有元素的值都相同,還可以這樣定義:
let a = [3; 5];
複製程式碼
這表示定義一個長度為5的陣列,每個元素都是3。
程式碼寫在哪——函式
函式在每個程式語言中都是基本的概念,因此我們不做過多贅述。Rust定義函式的方法是:
fn main() {
let a = 1;
let b = 2;
let sum = add(a,b);
println!("The value of sum is: {}",sum);
}
fn add(x: i32,y: i32) -> i32 {
x + y
}
複製程式碼
Rust在定義函式時,需要指定引數的名稱和型別和返回值的型別。而返回值只能是表示式。作為函式返回的表示式是不能以分號結尾的。
該往哪走——流程控制
Rust的流程控制語句包括條件控制語句和迴圈語句。條件控制語句有if,迴圈語句包括loop、while和for。
if
Rust中if的條件必須是bool型別,它不像js中,會自動將變數轉換成bool型別。此外,if還可以用於let語句中。例如:
let number = if condition {
5
} else {
6
};
複製程式碼
這種方式需要注意的是,每個表示式中返回的值必須是同一型別的。
loop
loop迴圈中,如果沒有break或者是手動停止,那麼它會一直迴圈下去。寫法很簡單。
loop {
println!("again!");
}
複製程式碼
loop的用處是它可以有返回值
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2;
}
};
複製程式碼
while
while迴圈是當條件成立時進入迴圈。
while number != 0 {
// do something
}
複製程式碼
for
當我們需要遍歷陣列時,可以使用for迴圈。
for element in a.iter() {
println!("the value is: {}",element);
}
複製程式碼
總結
以上,是Rust的一些基本概念。和其他的程式語言大同小異,記得一些特殊的地方就好,例如變數的不可變性。我們還有一些資料型別沒有涉及,比如vector,String等,這些會在後面詳細講解。
至此,我已經又向下挖了一層了。不知道你入坑了沒有?已經入坑的同學還請麻煩幫忙往外刨(分)土(享)。