變量
整數
- 無符號整數以u開頭
- 有符號整數以i開頭
- 對于Rust默認整數是i32
- 對于整數溢出
- 開發模式中編譯會檢測溢出,如果溢出會導致程序panic
- 發布模式中編譯不會檢查可能會導致的溢出,如果運行時發生溢出,會執行環繞操作保證數值在范圍內且程序不會panic
單精度浮點類型
- f32
雙精度浮點類型
- f64
- Rust采用f64作為默認的浮點類型
布爾類型
- bool:占用1字節大小
字符類型
- char:占用4字節大小
- 使用單引號
復合類型
- 元組(Tuple):可以將多個類型的多個值放在一個類型里,長度固定一旦創建無法修改
- 創建:在小括號中,將值用逗號分開
- Tuple中的每一個位置都對應一個類型,Tuple中各元素的類型不必相同
- 訪問Tuple元素使用 .下標 進行訪問
- 數組:數組元素類型必須相同,數組長度固定,在棧上存儲
- 創建:在中括號中,將值用逗號分開
let a:[i32; 5] = [1, 2, 3, 4, 5]; let a = [3; 5];// 等于let a = [3, 3, 3, 3, 3];
- 使用 [下標] 訪問數組元素
- 下標越界編譯會通過,但是運行會panic
字符串
- String在堆內存上分配,能夠存儲在編譯時未知數量的文本
- String類型的值可以修改,但是字符串字面值不能修改,因為字符串字面值在編譯時就知道它的內容了,其文本內容直接被硬編碼到最終的可執行文件里(速度快,高效,是因為它的不可變性),對于String類型,為了支持可變性,需要在堆內存上分配內存來保存編譯時未知的文本內容(操作系統必須在運行時來請求內存,這步通過調用String::from來實現,當用完String后,需要使用某種方式將內存返回給操作系統)
let mut s = String::from("abcdefg");
s.push_str("hi");
// s -> abcdefghi
- 一個String由三個部分組成(這部分存放在棧中,字符串內容存放在堆中)
- 指向內容的指針
- 長度(存放字符串內容所需的字節數)
- 容量(從操作系統獲得的總字節數)
let s1 = String::from("123");
let s2 = s1;
// s1將不能再被使用
let s1 = String::from("123");
let s2 = s1.clone();
// s1和s2獨立
Copy trait
- 如果一個類型實現了Copy這個trait,那么舊的變量在賦值后任然可以使用
- 如果一個類型或者該類型的一部分實現了Drop trait,那么Rust不允許讓它再去實現Copy trait了
一些擁有Copy trait的類型
- 任何簡單標量的組合類型都可以是Copy的
- 任何需要分配內存或者某種資源的都不是Copy的
- 一些擁有Copy trait的類型:
- 所有的整數類型,例如u32
- bool
- char
- 所有的浮點類型
- Tuple(元組)要求其所有字段都是Copy的
函數
聲明
使用 fn
關鍵字
Rust命名規范(snake case):
- 針對函數和變量名,所有字母都是小寫,單詞之間使用下劃線分開
參數
- 在函數簽名里,必須聲明每個參數的類型
語句/表達式
- 函數體由一系列語句組成,可選的由一個表達式結束
- Rust是一個基于表達式的語言
- 語句是執行一些動作的指令
- 表達式會計算產生一個值
- 函數的定義也是語句
- 語句沒有返回值,所以不可以使用let將一個語句賦給一個變量
// 塊表達式
let y = {let x = 1;x + 3
}
// y等于塊表達式最后一個值即x + 3 = 4
// 如果x + 3后加上分號,那么x + 3就變成語句了,分號后面沒有返回值,默認就是空的Tuple即()
fn f() -> i32{5
}fn main(){let res = f();// res為5
}
返回值
- 在
->
符號后邊聲明函數返回值的類型,但是不可以為返回值命名 - 在Rust里面,返回值就是函數體最后一個表達式的值
- 若想提前返回,需使用
return
關鍵字,并指定一個值 - 大多數函數都是默認使用最后一個表達式作為返回值
注釋
- 單行注釋
// 這是單行注釋
- 多行注釋
// 這是
// 多行注釋/*** 這是* 多行注釋
**/
控制流
if表達式
- if
表達式
的條件必須是bool類型,否則會報錯(Rust不會自動類型轉換)
if 3 < 5{// true
}else{// false
}
let a = if 1 < 3 { 1 } else { 6 };
// a = 1;
loop表達式
- loop表達式里的代碼會一直循環執行,直到break
let mut cnt = 0;
let res = loop{cnt += 1;if cnt == 10 {break cnt;}
}
// res = 10;
while循環
let mut cnt = 100;
while cnt > 0 {cnt -= 1;
}
// res = 0;
for循環
- 安全,簡潔,用得較多
let a = [1, 2, 3, 4];for tem in a.iter() {println!("{}", tem);
}
Range
- 標準庫提供
- 指定開始數字和結束數字,Range可以生成他們之間的數字(不含結束)
- rev方法可以反轉Range
// (1..4)生成1 - 3 之間的數字
// rev()反轉,即 3 - 1之間的數字
// 輸出 3 2 1
for tem in (1..4).rev() {println!("{}", tem);
}
所有權
- Rust的核心特性就是所有權
- 所有程序在運行時都必須管理它們使用計算機內存的方式
- 有些語言有垃圾收集機制,在程序運行時,他們會不斷地尋找不再使用的內存
- 在其他語言中,程序員必須顯示地分配和釋放內存
- Rust采用了第三種方式
- 內存是通過一個所有權系統來管理的,其中包含一組編譯器在編譯時檢查的規則
- 當程序運行時,所有權特性不會減慢程序的運行速度
棧內存(Stack)/堆內存(Heap)
- 棧內存
- 后進先出
- 所有存儲在棧內存上的數據必須擁有已知的固定大小
- 編譯時大小未知的數據或運行時大小可能發送改變的數據必須放在堆內存上
- 堆內存
- 內存組織性差一些
- 當把數據放入堆內存時,會請求一定數量的空間
- 操作系統在堆內存里找到一塊足夠大空間,把他標記為在用,并返回一個指針,也就是這個空間的地址
- 這個過程叫做在堆內存上進行內存分配,有時僅稱為
分配
- 內存組織性差一些
- 把值壓到棧內存上不叫分配
- 因為指針是已知固定大小的,可以把指針存放在棧內存上
- 但如果想要實際數據,必須使用指針來定位
- 把數據壓到棧內存上要比在堆內存上分配快得多
- 因為操作系統不需要尋找用來存儲新數據的空間,那個位置永遠都在棧內存的頂端
- 在堆內存上分配空間需要做更多的工作
- 操作系統需要找到一個足夠大的空間來存放數據,然后要做好記錄方便下次分配
- 訪問堆內存中的數據要比訪問棧內存中的數據慢,因為需要通過指針才能找到堆內存中的數據
- 對于現代的處理器來說,由于緩存的緣故,如果指令在內存中跳轉的次數越少,那么速度就越快
函數調用
- 調用函數時,值被傳入到函數(也包括指向堆內存的指針)。函數本地的變量被壓到棧內存上。當函數結束后,這些值會從棧內存上彈出。
所有權存在的原因
-
所有權解決的問題
- 跟蹤代碼的哪些部分正在使用堆內存的哪些數據
- 最小化堆內存上的重復數據量
- 清理堆內存上未使用的數據以避免空間不足
-
所有權規則
- 每個值都有一個變量,這個變量是該值的所有者
- 每個值同時只能有一個所有者
- 當所有者超出作用域(scope)時,該值將被刪除
返回值與作用域
- 函數在返回值的過程中同樣也會發生所有權的轉移
- 一個變量的所有權總是遵循同樣的模式:
- 把一個值賦值給其他變量時就發生移動
- 當一個包含堆內存數據的變量離開作用域時,它的值就會被drop函數清理,除非數據的所有權移動到另一個變量上了
引用
- &表示引用,允許你引用某些值而不取得其所有權
借用
- 把引用作為函數參數這個行為叫做借用
- 不可以修改借用的東西
- 和變量一樣,引用默認也是不可變的
可變引用
#![allow(warnings)]
use std::io;
use std::error::Error;
use std::boxed::Box;
use std::convert::TryInto;
use std::cmp::Ordering;
use std::cmp::min;
use std::cmp::max;fn calculate_length(s: &mut String) -> usize {s.push_str(" LTPP");s.len()
}fn main() -> Result<(), Box<dyn Error>> {let mut s1: String = String::from("SQS");println!("{} {}", s1, s1.len());let len: usize = calculate_length(&mut s1);println!("{} {}", s1, len);Ok(())
}
運行結果
SQS 3
SQS LTPP 8
-
在特定作用域內,對于某一塊數據,只能有一個可變的引用
- 好處:在編譯時解決數據競爭
-
以下三種行為會發生數據競爭
- 兩個或多個指針同時訪問一個數據
- 至少有一個指針用于寫數據
- 沒有使用任何機制來同步數據的訪問
-
可以通過創建新的作用域,來允許非同時的創建多個可變引用
#![allow(warnings)]
use std::io;
use std::error::Error;
use std::boxed::Box;
use std::convert::TryInto;
use std::cmp::Ordering;
use std::cmp::min;
use std::cmp::max;fn main() -> Result<(), Box<dyn Error>> {let mut s1: String = String::from("SQS");{let s2: String = String::from("LTPP");// s2 離開當前作用域后被銷毀}let s1_2 = &mut s1; // s1_2為SQSprintln!("{}", s1_2);let s2_2 = &mut s2; // 報錯,s2在當前作用域不存在println!("{}", s1_2); Ok(())
}
運行結果
編譯出錯!
error[E0425]: cannot find value `s2` in this scope--> /main.rs:19:21|
19 | let s2_2 = &mut s2; // 報錯,s2在當前作用域不存在| ^^ help: a local variable with a similar name exists: `s1`error: aborting due to previous errorFor more information about this error, try `rustc --explain E0425`.
- 不可同時擁有一個可變引用和一個不可變引用
- 多個不可變的引用是可以的
#![allow(warnings)]
use std::io;
use std::error::Error;
use std::boxed::Box;
use std::convert::TryInto;
use std::cmp::Ordering;
use std::cmp::min;
use std::cmp::max;fn main() -> Result<(), Box<dyn Error>> {let mut s: String = String::from("SQS");let r1 = &s;let r2 = &s;let s1 = &mut s;println!("{} {} {}", r1, r2, s1);Ok(())
}
運行結果
編譯出錯!
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable--> /main.rs:14:14|
12 | let r1 = &s;| -- immutable borrow occurs here
13 | let r2 = &s;
14 | let s1 = &mut s;| ^^^^^^ mutable borrow occurs here
15 | println!("{} {} {}", r1, r2, s1);| -- immutable borrow later used hereerror: aborting due to previous errorFor more information about this error, try `rustc --explain E0502`.
懸空引用
- 懸空指針:一個指針引用了內存中的某個地址,而這塊內存可能已經釋放并分配給其他人使用了
#![allow(warnings)]
use std::io;
use std::error::Error;
use std::boxed::Box;
use std::convert::TryInto;
use std::cmp::Ordering;
use std::cmp::min;
use std::cmp::max;fn dangle() -> &String {let s: String = String::from("SQS");&s
}fn main() -> Result<(), Box<dyn Error>> {let r = dangle();Ok(())
}
運行結果ust
編譯出錯!
error[E0106]: missing lifetime specifier--> /main.rs:10:16|
10 | fn dangle() -> &String {| ^ expected named lifetime parameter|= help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
help: consider using the `'static` lifetime|
10 | fn dangle() -> &'static String {| +++++++error: aborting due to previous errorFor more information about this error, try `rustc --explain E0106`.
-
Rust里,編譯器可保證引用永遠都不是懸空引用
- 如果引用了某些數據,編譯器保證在引用離開作用域之前數據不會離開作用域
-
引用的規則,任意給定的時刻,只能滿足以下情況之一
- 一個可變的引用
- 任意數量不可變的引用