溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

如何掌握Rust語言

發(fā)布時間:2021-10-21 10:38:44 來源:億速云 閱讀:153 作者:iii 欄目:編程語言

本篇內(nèi)容介紹了“如何掌握Rust語言”的有關(guān)知識,在實(shí)際案例的操作過程中,不少人都會遇到這樣的困境,接下來就讓小編帶領(lǐng)大家學(xué)習(xí)一下如何處理這些情況吧!希望大家仔細(xì)閱讀,能夠?qū)W有所成!

一、Rust開發(fā)環(huán)境指南

1.1 Rust代碼執(zhí)行

根據(jù)編譯原理知識,編譯器不是直接將源語言翻譯為目標(biāo)語言,而是翻譯為一種“中間語言”,編譯器從業(yè)人員稱之為“IR”--指令集,之后再由中間語言,利用后端程序和設(shè)備翻譯為目標(biāo)平臺的匯編語言。

Rust代碼執(zhí)行:

1) Rust代碼經(jīng)過分詞和解析,生成AST(抽象語法樹)。

2) 然后把AST進(jìn)一步簡化處理為HIR(High-level IR),目的是讓編譯器更方便的做類型檢查。

3) HIR會進(jìn)一步被編譯為MIR(Middle IR),這是一種中間表示,主要目的是:

a) 縮短編譯時間;

b) 縮短執(zhí)行時間;

c) 更精確的類型檢查。

4) 最終MIR會被翻譯為LLVM IR,然后被LLVM的處理編譯為能在各個平臺上運(yùn)行的目標(biāo)機(jī)器碼。

IR:中間語言

HIR:高級中間語言

MIR:中級中間語言

LLVM :Low Level Virtual Machine,底層虛擬機(jī)。

LLVM是構(gòu)架編譯器(compiler)的框架系統(tǒng),以C++編寫而成,用于優(yōu)化以任意程序語言編寫的程序的編譯時間(compile-time)、鏈接時間(link-time)、運(yùn)行時間(run-time)以及空閑時間(idle-time)

無疑,不同編譯器的中間語言IR是不一樣的,而IR可以說是集中體現(xiàn)了這款編譯器的特征:他的算法,優(yōu)化方式,匯編流程等等,想要完全掌握某種編譯器的工作和運(yùn)行原理,分析和學(xué)習(xí)這款編譯器的中間語言無疑是重要手段。

由于中間語言相當(dāng)于一款編譯器前端和后端的“橋梁”,如果我們想進(jìn)行基于LLVM的后端移植,無疑需要開發(fā)出對應(yīng)目標(biāo)平臺的編譯器后端,想要順利完成這一工作,透徹了解LLVM的中間語言無疑是非常必要的工作。

LLVM相對于gcc的一大改進(jìn)就是大大提高了中間語言的生成效率和可讀性, LLVM的中間語言是一種介于c語言和匯編語言的格式,他既有高級語言的可讀性,又能比較全面地反映計算機(jī)底層數(shù)據(jù)的運(yùn)算和傳輸?shù)那闆r,精煉而又高效。

1.1.1 MIR

MIR是基于控制流圖(Control Flow Graph,CFG)的抽象數(shù)據(jù)結(jié)構(gòu),它用有向圖(DAG)形式包含了程序執(zhí)行過程中所有可能的流程。所以將基于MIR的借用檢查稱為非詞法作用域的生命周期。

MIR由一下關(guān)鍵部分組成:

  • 基本塊(Basic block,bb),他是控制流圖的基本單位,

? 語句(statement)

? 終止句(Terminator)

  • 本地變量,占中內(nèi)存的位置,比如函數(shù)參數(shù)、局部變量等。

  • 位置(Place),在內(nèi)存中標(biāo)識未知的額表達(dá)式。

  • 右值(RValue),產(chǎn)生值的表達(dá)式。

具體的工作原理見《Rust編程之道》的第158和159頁。

可以在http://play.runst-lang.org中生成MIR代碼。

1.1 Rust安裝

? 方法一:見Rust官方的installation章節(jié)介紹。

實(shí)際上就是調(diào)用該命令來安裝即可:curl https://sh.rustup.rs -sSf | sh

? 方法二:下載離線的安裝包來安裝,具體的可見Rust官方的Other Rust Installation Methods章節(jié)。

1.2 Rust編譯&運(yùn)行

1.2.1 Cargo包管理

Cargo是Rust中的包管理工具,第三方包叫做crate

Cargo一共做了四件事:

  • l 使用兩個元數(shù)據(jù)(metadata)文件來記錄各種項(xiàng)目信息

  • l 獲取并構(gòu)建項(xiàng)目的依賴關(guān)系

  • l 使用正確的參數(shù)調(diào)用rustc或其他構(gòu)建工具來構(gòu)建項(xiàng)目

  • l 為Rust生態(tài)系統(tǒng)開發(fā)建議了統(tǒng)一標(biāo)準(zhǔn)的工作流

Cargo文件:

  • Cargo.lock:只記錄依賴包的詳細(xì)信息,不需要開發(fā)者維護(hù),而是由Cargo自動維護(hù)

  • Cargo.toml:描述項(xiàng)目所需要的各種信息,包括第三方包的依賴

cargo編譯默認(rèn)為Debug模式,在該模式下編譯器不會對代碼進(jìn)行任何優(yōu)化。也可以使用--release參數(shù)來使用發(fā)布模式。release模式,編譯器會對代碼進(jìn)行優(yōu)化,使得編譯時間變慢,但是代碼運(yùn)行速度會變快。

官方編譯器rustc,負(fù)責(zé)將rust源碼編譯為可執(zhí)行的文件或其他文件(.a、.so、.lib等)。例如:rustc box.rs

Rust還提供了包管理器Cargo來管理整個工作流程。例如:

  • lcargo newfirst_pro_create :創(chuàng)建名為first_pro_create的項(xiàng)目

  • lcargo new --libfirst_lib_create :創(chuàng)建命令first_lib_create的庫項(xiàng)目

  • lcargo doc

  • lcargo doc --open

  • lcargo test

  • lcargo test -- --test-threads=1

  • lcargo build

  • lcargo build --release

  • lcargo run

  • lcargo install --path

  • lcargo uninstallfirst_pro_create

  • lcargo new –bin use_regex

如何掌握Rust語言

1.2.2 使用第三方包

Rust可以在Cargo.toml中的[dependencies]下添加想依賴的包來使用第三方包。

然后在src/main.rssrc/lib.rs文件中,使用extern crate命令聲明引入該包即可使用。

例如:

如何掌握Rust語言

值得注意的是,使用extern crate聲明包的名稱是linked_list,用的是下劃線_”,而在Cargo.toml中用的是連字符-”。其實(shí)Cargo默認(rèn)會把連字符轉(zhuǎn)換成下劃線

Rust也不建議以“-rs”或“_rs”為后綴來命名包名,而且會強(qiáng)制性的將此后綴去掉。

具體的見《Rust編程之道》的第323頁。

1.4 Rust常用命令

1.5 Rust命令規(guī)范

函數(shù): 蛇形命名法(snake_case),例如:func_name()

文件名: 蛇形命名法(snake_case),例如file_name.rs、main.rs

臨時變量名:蛇形命名法(snake_case)

全局變量名

結(jié)構(gòu)體: 大駝峰命名法,例如:struct FirstName { name: String}

enum類型: 大駝峰命名法。

關(guān)聯(lián)常量:常量名必須全部大寫。什么是關(guān)聯(lián)常量見《Rust編程之道》的第221頁。

? Cargo默認(rèn)會把連字符-”轉(zhuǎn)換成下劃線_”。

? Rust也不建議以“-rs”或“_rs”為后綴來命名包名,而且會強(qiáng)制性的將此后綴去掉。

二、Rust語法

2.1 疑問&總結(jié)

2.1.1 Copy語義 && Move語義(Move語義必須轉(zhuǎn)移所有權(quán))

類型越來越豐富,值類型和引用類型難以描述全部情況,所以引入了:

值語義(Value Semantic)

復(fù)制以后,兩個數(shù)據(jù)對象擁有的存儲空間是獨(dú)立的,互不影響。

基本的原生類型都是值語義,這些類型也被稱為POD(Plain old data)。POD類型都是值語義,但是值語義類型并不一定都是POD類型。

具有值語義的原生類型,在其作為右值進(jìn)行賦值操作時,編譯器會對其進(jìn)行按位復(fù)制。

引用語義(Reference Semantic)

復(fù)制以后,兩個數(shù)據(jù)對象互為別名。操作其中任意一個數(shù)據(jù)對象,則會影響另外一個。

智能指針Box<T>封裝了原生指針,是典型的引用類型。Box<T>無法實(shí)現(xiàn)Copy,意味著它被rust標(biāo)記為了引用語義,禁止按位復(fù)制。

引用語義類型不能實(shí)現(xiàn)Copy,但可以實(shí)現(xiàn)Clone的clone方法,以實(shí)現(xiàn)深復(fù)制。

在Rust中,可以通過是否實(shí)現(xiàn)Copy trait來區(qū)分?jǐn)?shù)據(jù)類型的值語義引用語義。但為了更加精準(zhǔn),Rust也引用了新的語義:復(fù)制(Copy)語義移動(Move)語義。

Copy語義:對應(yīng)值語義,即實(shí)現(xiàn)了Copy的類型在進(jìn)行按位復(fù)制時是安全的。

Move語義:對應(yīng)引用語義。在Rust中不允許按位復(fù)制,只允許移動所有權(quán)

2.1.2 哪些實(shí)現(xiàn)了Copy

結(jié)構(gòu)體 :當(dāng)成員都是復(fù)制語義類型時,不會自動實(shí)現(xiàn)Copy。

枚舉體 :當(dāng)成員都是復(fù)制語義類型時,不會自動實(shí)現(xiàn)Copy。

結(jié)構(gòu)體 && 枚舉體

1) 所有成員都是復(fù)制語義類型時,需要添加屬性#[derive(Debug,Copy,Clone)]來實(shí)現(xiàn)Copy。

2) 如果有移動語義類型的成員,則無法實(shí)現(xiàn)Copy。

元組類型 :本身實(shí)現(xiàn)了Copy。如果元素均為復(fù)制語義類型,則默認(rèn)是按位復(fù)制,否則執(zhí)行移動語義。

字符串字面量 &str: 支持按位復(fù)制。例如:c = “hello”; 則c就是字符串字面量。

2.1.3 哪些未實(shí)現(xiàn)Copy

字符串對象String :to_string() 可以將字符串字面量轉(zhuǎn)換為字符串對象。

2.1.4 哪些實(shí)現(xiàn)了Copy trait

原生整數(shù)類型

對于實(shí)現(xiàn)Copy的類型,其clone方法只需要簡單的實(shí)現(xiàn)按位復(fù)制即可。

2.1.5 哪些未實(shí)現(xiàn)Copy trait

Box<T>

實(shí)現(xiàn)了Copy trait,有什么作用?

實(shí)現(xiàn)Copy trait的類型同時擁有復(fù)制語義,在進(jìn)行賦值或者傳入函數(shù)等操作時,默認(rèn)會進(jìn)行按位復(fù)制。

? 對于默認(rèn)可以安全的在棧上進(jìn)行按位復(fù)制的類型,就只需要按位復(fù)制,也方便管理內(nèi)存。

? 對于默認(rèn)只可在堆上存儲的數(shù)據(jù),必須進(jìn)行深度復(fù)制。深度復(fù)制需要在堆內(nèi)存中重新開辟空間,這會帶來更多的性能開銷。

2.1.6 哪些是在棧上的?哪些是在堆上的?

2.1.7 let綁定

? Rust聲明的綁定默認(rèn)為不可變

? 如果需要修改,可以用mut來聲明綁定是可變的。

2.2 數(shù)據(jù)類型

很多編程語言中的數(shù)據(jù)類型是分為兩類:

值類型

一般是指可以將數(shù)據(jù)都保存在同一位置的類型。例如數(shù)值、布爾值、結(jié)構(gòu)體等都是值類型。

值類型有:

  • l原生類型

  • l結(jié)構(gòu)體

  • l枚舉體

引用類型

會存在一個指向?qū)嶋H存儲區(qū)的指針。比如通常一些引用類型會將數(shù)據(jù)存儲在堆中,而棧中只存放指向堆中數(shù)據(jù)的地址(指針)。

引用類型有:

  • l普通引用類型

  • l原生指針類型

2.2.1 基本數(shù)據(jù)類型

布爾類型

bool類型只有兩個值:truefalse。

基本數(shù)字類型

主要關(guān)注取值范圍,具體的見《Rust編程之道》的第26頁。

字符類型

單引號來定義字符(char)類型。字符類型代表一個Unicode標(biāo)量值,每個字節(jié)占4個字節(jié)。

數(shù)組類型

數(shù)組的類型簽名為[T; N]。T是一個泛型標(biāo)記,代表數(shù)組中元素的某個具體類型。N代表數(shù)組長度,在編譯時必須確定其值。

數(shù)組特點(diǎn):

  • l 大小固定

  • l 元素均為同類型

  • l 默認(rèn)不可變

切片類型

切片(Slice)類型是對一個數(shù)組的引用片段。在底層,切片代表一個指向數(shù)組起始位置的指針和數(shù)組長度。用[T]類型表示連續(xù)序列,那么切片類型就是&[T]&mut[T]。

具體的見《Rust編程之道》的第30頁。

str字符串類型

字符串類型str,通常是以不可變借用的形式存在,即&str(字符串切片)。

Rust將字符串分為兩種:

1) &str :固定長度字符串

2) String :可以隨意改變其長度。

&str字符串類型由兩部分組成:

1) 指向字符串序列的指針;

2) 記錄長度的值。

&str存儲于棧上,str字符串序列存儲于程序的靜態(tài)只讀數(shù)據(jù)段或者堆內(nèi)存中。

&str是一種胖指針。

never類型

never類型,即!。該類型用于表示永遠(yuǎn)不可能有返回值的計算類型。

其他(此部分不屬于基本數(shù)據(jù)類型)

此部分不屬于基本數(shù)據(jù)類型,由于編排問題,暫時先放在此處。

胖指針

胖指針:包含了動態(tài)大小類型地址信息和攜帶了長度信息的指針。

具體的見《Rust編程之道》的第54頁。

零大小類型

零大小類型(Zero sized Type,ZST)的特點(diǎn)是:它們的值就是其本身,運(yùn)行時并不占用內(nèi)存空間。

單元類型單元結(jié)構(gòu)體大小為零,由單元類型組成的數(shù)組大小也是零。

ZST類型代表的意義是“”。

底類型

底類型其實(shí)是介紹過的never類型,用嘆號!)表示。它的特點(diǎn)是:

  • l 沒有值

  • l 是其他任意類型的子類型

如果說ZST類型表示“”的話,那么底類型就表示“”。

底類型無值,而且它可以等價于任意類型。

具體的見《Rust編程之道》的第57頁。

2.2.2 復(fù)合數(shù)據(jù)類型

元組

Rust提供了4中復(fù)合數(shù)據(jù)類型:

  • l元組(Tuple)

  • l結(jié)構(gòu)體(Struct)

  • l枚舉體(Enum)

  • l聯(lián)合體(Union)

先來介紹元組。元組是一種異構(gòu)有限序列,形如(T,U,M,N)。所謂異構(gòu),就是指元組內(nèi)的元素可以是不同類型。所謂有限,是指元組有固定的長度。

  • 空元組: ()

  • l 只有一個值時,需要加逗號: (0,)

結(jié)構(gòu)體

Rust提供了3中結(jié)構(gòu)體:

  • l具名結(jié)構(gòu)體

  • l元組結(jié)構(gòu)體

  • l單元結(jié)構(gòu)體

例如:

具名結(jié)構(gòu)體

  struct People {      
    name: &’static str,
}

元組結(jié)構(gòu)體:字段沒有名稱,只有類型:

struct Color(i32, i32, i32);

當(dāng)一個元組結(jié)構(gòu)體只有一個字段的時候,稱為New Type模式。例如:

  struct Integer(u32);

單元結(jié)構(gòu)體:沒有任何字段的結(jié)構(gòu)體。單元結(jié)構(gòu)體實(shí)例就是其本身。

struct Empty;

結(jié)構(gòu)體更新語法

使用Struct更新語法(..)從其他實(shí)例創(chuàng)建新實(shí)例。當(dāng)新實(shí)例使用舊實(shí)例的大部分值時,可以使用struct update語法。 例如:

#[derive(Debug,Copy,Clone)]
struct Book<’a> {
name: &’a str,
isbn:  i32,
version: i32,
}
let book = Book {
    name: “Rust編程之道”,  isbn: 20181212, version: 1
};
let book2 = Book {version: 2, ..book};

注:

  • l 如果結(jié)構(gòu)體使用了移動語義的成員字段,則不允許實(shí)現(xiàn)Copy。

  • l Rust不允許包含了String類型字段的結(jié)構(gòu)體實(shí)現(xiàn)Copy。

  • l 更新語法會轉(zhuǎn)移字段的所有權(quán)。

枚舉體

該類型包含了全部可能的情況,可以有效的防止用戶提供無效值。例如:

enum Number {
    Zero,   
    One,    
}

Rust還支持?jǐn)y帶類型參數(shù)的枚舉體。這樣的枚舉值本質(zhì)上屬于函數(shù)類型,他可以通過顯式的指定類型來轉(zhuǎn)換為函數(shù)指針類型。例如:

enum IpAddr {          
    V4(u8, u8, u8, u8),
    V6(String),         
}

枚舉體在Rust中屬于非常重要的類型之一。例如:Option枚舉類型。

聯(lián)合體

2.2.3 常用集合類型

線性序列:向量

在Rust標(biāo)準(zhǔn)庫std::collections模塊下有4中通用集合類型,分別如下:

  • 線性序列向量(Vec)、雙端隊列(VecDeque)、鏈表(LinkedList)

  • Key-Value映射表無序哈希表(HashMap)、有序映射表(BTreeMap)

  • 集合類型無序集合(HashSet)、有序集合(BTreeSet)

  • 優(yōu)先隊列二叉堆(BinaryHeap)

具體的見《Rust編程之道》的第38頁和271頁。

向量也是一種數(shù)組,和基本數(shù)據(jù)類型中的數(shù)組的區(qū)別在于:向量可動態(tài)增長。

示例:

  let mut v1 = vec![];
let mut v2 = vec![0; 10];
let mut v3 = Vec::new();

vec!是一個宏,用來創(chuàng)建向量字面量。

線性序列:雙端隊列

雙端隊列(Double-ended Queue,縮寫Deque)是一種同時具有隊列(先進(jìn)先出)和棧(后進(jìn)先出)性質(zhì)的數(shù)據(jù)結(jié)構(gòu)。

雙端隊列中的元素可以從兩端彈出,插入和刪除操作被限定在隊列的兩端進(jìn)行。

示例:

  use std::collections::VecDeque;
  let mut buf = VecDeque::new();
buf.push_front(1);             
buf.get(0);                    
buf.push_back(2);

線性序列:鏈表

Rust提供的鏈表是雙向鏈表,允許在任意一端插入或彈出元素。最好使用Vec或VecDeque類型,他們比鏈表更加快速,內(nèi)存訪問效率更高。

示例:

  use std::collections::LinkedList;
  let mut list = LinkedList::new();
list.push_front(‘a(chǎn)’);             
list.append(&mut list2);         
list.push_back(‘b’);

Key-Value映射表:HashMap和BTreeMap

  • HashMap<K, V> => 無序

  • BTreeMap<K, V> => 有序

其中HashMap要求key是必須可哈希的類型,BTreeMap的key必須是可排序的。

Value必須是在編譯期已知大小的類型。

示例:

  use std::collections::BTreeMap;
use std::collections::HashMap;
  let mut hmap = HashMap::new();
let mut bmap = BTreeMap::new();
hmap.insert(1,”a”);           
bmap.insert(1,”a”);

集合:HashSet和BTreeSet

HashSet<K>BTreeSet<K>其實(shí)就是HashMap<K, V>BTreeMap<K, V>把Value設(shè)置為空元組的特定類型。

  • l 集合中的元素應(yīng)該是唯一的。

  • HashSet中的元素都是可哈希的類型,BTreeSet中的元素必須是可排序的。

  • HashSet應(yīng)該是無序的,BTreeSet應(yīng)該是有序的。

示例:

  use std::collections::BTreeSet;
use std::collections::HashSet;
  let mut hset = HashSet::new();   
let mut bset = BTreeSet::new();
hset.insert(”This is a hset.”);
bset.insert(”This is a bset”);

優(yōu)先隊列:BinaryHeap

Rust提供的優(yōu)先隊列是基于二叉最大堆(Binary Heap)實(shí)現(xiàn)的。

示例:

use std::collections::BinaryHeap;
  let mut heap = BinaryHeap::new();
heap.peek();                           => peek是取出堆中最大的元素
heap.push(98);

容量(Capacity)和大?。⊿ize/Len)

無論是Vec還是HashMap,使用這些集合容器類型,最重要的是理解容量(Capacity)和大?。⊿ize/Len)

容量是指為集合容器分配的內(nèi)存容量。

大小是指集合中包含的元素數(shù)量。

2.2.4 Rust字符串

Rust字符串分為以下幾種類型:

  • str:表示固定長度的字符串

  • String:表示可增長的字符串

  • CStr:表示由C分配而被Rust借用的字符串。這是為了兼容windows系統(tǒng)。

  • CString:表示由Rust分配且可以傳遞給C函數(shù)使用的C字符串,同樣用于和C語言交互。

  • OsStr:表示和操作系統(tǒng)相關(guān)的字符串。這是為了兼容windows系統(tǒng)。

  • OsString:表示OsStr的可變版本。與Rust字符串可以相互交換。

  • Path:表示路徑,定義于std::path模塊中。Path包裝了OsStr。

  • PathBuf:跟Path配對,是path的可變版本。PathBuf包裝了OsString。

str屬于動態(tài)大小類型(DST),在編譯期并不能確定其大小。所以在程序中最常見的是str的切片(Slice)類型&str。

&str代表的是不可變的UTF-8字節(jié)序列,創(chuàng)建后無法再為其追加內(nèi)容或更改其內(nèi)容。&str類型的字符串可以存儲在任意地方:

? 靜態(tài)存儲區(qū)

? 堆分配

? 棧分配

具體的見《Rust編程之道》的第249頁。

String類型本質(zhì)是一個成員變量為Vec<u8>類型的結(jié)構(gòu)體,所以它是直接將字符內(nèi)容存放于堆中的。

String類型由三部分組成:

? 執(zhí)行堆中字節(jié)序列的指針(as_ptr方法)

? 記錄堆中字節(jié)序列的字節(jié)長度(len方法)

? 堆分配的容量(capacity方法)

2.2.4.1 字符串處理方式

Rust中的字符串不能使用索引訪問其中的字符,可以通過byteschars兩個方法來分別返回按字節(jié)按字符迭代的迭代器。

Rust提供了另外兩種方法:getget_mut來通過指定索引范圍來獲取字符串切片。

具體的見《Rust編程之道》的第251頁。

2.2.4.2 字符串修改

? 追加字符串:pushpush_str,以及extend迭代器

? 插入字符串:insertinsert_str

? 連接字符串:String實(shí)現(xiàn)了Add<&str>AddAssign<&str>兩個trait,所以可以使用“+”和“+=”來連接字符串

? 更新字符串:通過迭代器或者某些unsafe的方法

? 刪除字符串:remove、pop、truncatecleardrain

具體的見《Rust編程之道》的第255頁。

2.2.4.3 字符串的查找

Rust總共提供了20個方法涵蓋了以下幾種字符串匹配操作:

? 存在性判斷

? 位置匹配

? 分割字符串

? 捕獲匹配

? 刪除匹配

? 替代匹配

具體的見《Rust編程之道》的第256頁。

2.2.4.4 類型轉(zhuǎn)換

parse:將字符串轉(zhuǎn)換為指定的類型

format!宏:將其他類型轉(zhuǎn)成成字符串

2.2.5 格式化規(guī)則

  • l 填充字符串寬度:{:5},5是指寬度為5

  • l 截取字符串:{:.5}

  • l 對齊字符串:{:>}{:^}、{:<},分別表示左對齊、位于中間右對齊

  • l{:*^5} 使用*替代默認(rèn)空格來填充



  • l 符號+:表示強(qiáng)制輸出整數(shù)的正負(fù)符號

  • l 符號#:用于顯示進(jìn)制的前綴。比如:十六進(jìn)制0x

  • l 數(shù)字0:用于把默認(rèn)填充的空格替換成數(shù)字0

  • {:x} :轉(zhuǎn)換成16進(jìn)制輸出

  • {:b} :轉(zhuǎn)換成二進(jìn)制輸出

  • l{:.5}:指定小數(shù)點(diǎn)后有效位是5

  • {:e}:科學(xué)計數(shù)法表示

具體的見《Rust編程之道》的第265頁。

2.2.6 原生字符串聲明語法:r”…”

原生字符串聲明語法(r”…”)可以保留原來字符串中的特殊符號。

具體的見《Rust編程之道》的第270頁。

2.2.7 全局類型

Rust支持兩種全局類型:

  • 普通常量(Constant)

  • 靜態(tài)變量(Static)

區(qū)別:

  • l 都是在編譯期求值的,所以不能用于存儲需要動態(tài)分配內(nèi)存的類型

  • l 普通常量可以被內(nèi)聯(lián)的,它沒有確定的內(nèi)存地址,不可變

  • l 靜態(tài)變量不能被內(nèi)聯(lián),它有精確的內(nèi)存地址,擁有靜態(tài)生命周期

  • l 靜態(tài)變量可以通過內(nèi)部包含UnsafeCell等容器實(shí)現(xiàn)內(nèi)部可變性

  • l 靜態(tài)變量還有其他限制,具體的見《Rust編程之道》的第326頁

  • l 普通常量也不能引用靜態(tài)變量

在存儲的數(shù)據(jù)比較大需要引用地址具有可變性的情況下使用靜態(tài)變量。否則,應(yīng)該優(yōu)先使用普通常量。

但也有一些情況是這兩種全局類型無法滿足的,比如想要使用全局的HashMap,在這種情況下,推薦使用lazy_static包。利用lazy_static包可以把定義全局靜態(tài)變量延遲到運(yùn)行時,而非編譯時。

2.3 trait

trait是對類型行為的抽象。trait是Rust實(shí)現(xiàn)零成本抽象的基石,它有如下機(jī)制:

  • l trait是Rust唯一的接口抽象方式;

  • l 可以靜態(tài)分發(fā),也可以動態(tài)分發(fā);

  • l 可以當(dāng)做標(biāo)記類型擁有某些特定行為的“標(biāo)簽”來使用。

示例:

  struct Duck;                
struct Pig;                 
trait Fly {                 
    fn fly(&self) -> bool;
}                            
impl Fly for Duck {        
    fn fly(&self) -> bool {
         return true;       
    }                         
}                            
impl Fly for Pig {         
    fn fly(&self) -> bool {
         return false;      
    }                        
}

靜態(tài)分發(fā)和動態(tài)分發(fā)的具體介紹可見《Rust編程之道》的第46頁。

trait限定

以下這些需要繼續(xù)深入理解第三章并總結(jié)。待后續(xù)繼續(xù)補(bǔ)充。

trait對象

標(biāo)簽trait

Copy trait

Deref解引用

as操作符

From和Into

2.4 指針

2.3.1 引用Reference

&& mut操作符來創(chuàng)建。受Rust的安全檢查規(guī)則的限制。

引用是Rust提供的一種指針語義。引用是基于指針的實(shí)現(xiàn),他與指針的區(qū)別是:指針保存的是其指向內(nèi)存的地址,而引用可以看做某塊內(nèi)存的別名(Alias)。

在所有權(quán)系統(tǒng)中,引用&x也可以稱為x的借用(Borrowing)。通過&操作符來完成所有權(quán)租借

2.3.2 原生指針(裸指針)

*const T*mut T。可以在unsafe塊下任意使用,不受Rust的安全檢查規(guī)則的限制。

2.3.3 智能指針

實(shí)際上是一種結(jié)構(gòu)體,只是行為類似指針。智能指針是對指針的一層封裝,提供了一些額外的功能,比如自動釋放堆內(nèi)存。

智能指針區(qū)別于常規(guī)結(jié)構(gòu)體的特性在于:它實(shí)現(xiàn)了DerefDrop這兩個trait。

Deref:提供了解引用能力

Drop:提供了自動析構(gòu)的能力

2.3.3.1 智能指針有哪些

智能指針擁有資源的所有權(quán),而普通引用只是對所有權(quán)的借用。

Rust中的值默認(rèn)被分配到棧內(nèi)存??梢酝ㄟ^Box<T>將值裝箱(在堆內(nèi)存中分配)。

String

? Vec

String類型和Vec類型的值都是被分配到堆內(nèi)存返回指針的,通過將返回的指針封裝來實(shí)現(xiàn)DerefDrop。

? Box<T>

Box<T>是指向類型為T的堆內(nèi)存分配值的智能指針。當(dāng)Box<T>超出作用域范圍時,將調(diào)用其析構(gòu)函數(shù),銷毀內(nèi)部對象,并自動釋放堆中的內(nèi)存。

? Arc<T>

? RC<T>

單線程引用計數(shù)指針,不是線程安全的類型。

可以將多個所有權(quán)共享給多個變量,每當(dāng)共享一個所有權(quán)時,計數(shù)就會增加一次。具體的見《Rust編程之道》的第149頁。

? Weak<T>

RC<T>的另一個版本。

通過clone方法共享的引用所有權(quán)稱為強(qiáng)引用,RC<T>是強(qiáng)引用。

Weak<T>共享的指針沒有所有權(quán),屬于弱引用。具體的見《Rust編程之道》的第150頁。

? Cell<T>

實(shí)現(xiàn)字段級內(nèi)部可變的情況。

適合復(fù)制語義類型。

? RefCell<T>

適合移動語義類型。

Cell<T>和RefCell<T>本質(zhì)上不屬于智能指針,只是提供內(nèi)不可變性的容器。

Cell<T>和RefCell<T>使用最多的場景就是配合只讀引用來使用。

具體的見《Rust編程之道》的第151頁。

Cow<T>

Copy on write:一種枚舉體的智能指針。Cow<T>表示的是所有權(quán)的“借用”和“擁有”。Cow<T>的功能是:以不可變的方式訪問借用內(nèi)容,以及在需要可變借用或所有權(quán)的時候再克隆一份數(shù)據(jù)。

Cow<T>旨在減少復(fù)制操作,提高性能,一般用于讀多寫少的場景。

Cow<T>的另一個用處是統(tǒng)一實(shí)現(xiàn)規(guī)范。

2.3.4 解引用deref

解引用會獲得所有權(quán)。

解引用操作符: *

哪些實(shí)現(xiàn)了deref方法

Box<T>:源碼見《Rust編程之道》的第147頁。

Cow<T>:意味著可以直接調(diào)用其包含數(shù)據(jù)的不可變方法。具體的要點(diǎn)可見《Rust編程之道》的第155頁。

?

Box<T >支持解引用移動, Rc<T>和Arc<T>智能指針不支持解引用移動。

2.4 所有權(quán)機(jī)制(ownership):

Rust中分配的每塊內(nèi)存都有其所有者,所有者負(fù)責(zé)該內(nèi)存的釋放和讀寫權(quán)限,并且每次每個值只能有唯一的所有者。

在進(jìn)行賦值操作時,對于可以實(shí)現(xiàn)Copy的復(fù)制語義類型,所有權(quán)并未改變。對于復(fù)合類型來說,是復(fù)制還是移動,取決于其成員的類型。

例如:如果數(shù)組的元素都是基本的數(shù)字類型,則該數(shù)組是復(fù)制語義,則會按位復(fù)制。

2.4.1 詞法作用域(生命周期)

match、forloop、whileif let、while let花括號、函數(shù)、閉包都會創(chuàng)建新的作用域,相應(yīng)綁定的所有權(quán)會被轉(zhuǎn)移,具體的可見《Rust編程之道》的第129頁。

函數(shù)體本身是獨(dú)立的詞法作用域:

? 當(dāng)復(fù)制語義類型作為函數(shù)參數(shù)時,會按位復(fù)制。

? 如果是移動語義作為函數(shù)參數(shù),則會轉(zhuǎn)移所有權(quán)。

2.4.2 非詞法作用域聲明周期

借用規(guī)則: 借用方的生命周期不能長于出借方的生命周期。用例見《Rust編程之道》的第157頁。

因?yàn)橐陨系囊?guī)則,經(jīng)常導(dǎo)致實(shí)際開發(fā)不便,所以引入了非詞法作用域生命周期(Non-Lexical Lifetime,NLL)來改善。

MIR是基于控制流圖(Control Flow Graph,CFG)的抽象數(shù)據(jù)結(jié)構(gòu),它用有向圖(DAG)形式包含了程序執(zhí)行過程中所有可能的流程。所以將基于MIR的借用檢查稱為非詞法作用域的生命周期。

2.4.2 所有權(quán)借用

使用可變借用的前提是:出借所有權(quán)的綁定變量必須是一個可變綁定。

在所有權(quán)系統(tǒng)中,引用&x也可以稱為x的借用(Borrowing)。通過&操作符來完成所有權(quán)租借。所以引用并不會造成綁定變量所有權(quán)的轉(zhuǎn)移。

引用在離開作用域之時,就是其歸還所有權(quán)之時。

? 不可變借用(引用)不能再次出借為可變借用。

? 不可變借用可以被出借多次。

? 可變借用只能出借一次。

? 不可變借用和可變借用不能同時存在,針對同一個綁定而言。

? 借用的生命周期不能長于出借方的生命周期。具體的舉例見《Rust編程之道》的第136頁。

核心原則:共享不可變,可變不共享。

因?yàn)?strong>解引用操作會獲得所有權(quán),所以在需要對移動語義類型(如&String)進(jìn)行解引用時需要特別注意。

2.4.3 生命周期參數(shù)

編譯器的借用檢查機(jī)制無法對跨函數(shù)的借用進(jìn)行檢查,因?yàn)楫?dāng)前借用的有效性依賴于詞法作用域。所以,需要開發(fā)者顯式的對借用的生命周期參數(shù)進(jìn)行標(biāo)注。

2.4.3.1 顯式生命周期參數(shù)

? 生命周期參數(shù)必須是以單引號開頭;

? 參數(shù)名通常都是小寫字母,例如:'a;

? 生命周期參數(shù)位于引用符號&后面,并使用空格來分割生命周期參數(shù)和類型。

標(biāo)注生命周期參數(shù)是由于borrowed pointers導(dǎo)致的。因?yàn)橛衎orrowed pointers,當(dāng)函數(shù)返回borrowed pointers時,為了保證內(nèi)存安全,需要關(guān)注被借用的內(nèi)存的生命周期(lifetime)。

標(biāo)注生命周期參數(shù)并不能改變?nèi)魏我玫纳芷陂L短,它只用于編譯器的借用檢查,來防止懸垂指針。即:生命周期參數(shù)的目的是幫助借用檢查器驗(yàn)證合法的引用,消除懸垂指針。

例如:

&i32;  ==> 引用
&'a i32;    ==> 標(biāo)注生命周期參數(shù)的引用
&'a mut i32;    ==> 標(biāo)注生命周期參數(shù)的可變引用
 
允許使用&'a str;的地方,使用&'static str;也是合法的。
 
對于'static:當(dāng)borrowed pointers指向static對象時需要聲明'static lifetime。
如:
static STRING: &'static str = "bitstring";

2.4.3.2 函數(shù)簽名中的生命周期參數(shù)

fn foo<'a>(s: &'a str, t: &'a str) -> &'a str;

函數(shù)名后的<'a>為生命周期參數(shù)的聲明。函數(shù)或方法參數(shù)的生命周期叫做輸入生命周期(input lifetime),而返回值的生命周期被稱為輸出生命周期(output lifetime)。

規(guī)則:

? 禁止在沒有任何輸入?yún)?shù)的情況下返回引用,因?yàn)闀斐蓱掖怪羔槨?/p>

? 從函數(shù)中返回(輸出)一個引用,其生命周期參數(shù)必須與函數(shù)的參數(shù)(輸入)相匹配,否則,標(biāo)注生命周期參數(shù)也毫無意義。

對于多個輸入?yún)?shù)的情況,也可以標(biāo)注不同的生命周期參數(shù)。具體的舉例見《Rust編程之道》的第139頁。

2.4.3.3 結(jié)構(gòu)體定義中的生命周期參數(shù)

結(jié)構(gòu)體在含有引用類型成員的時候也需要標(biāo)注生命周期參數(shù),否則編譯失敗。

例如:

struct Foo<'a> {
            part: &'a str,
}

這里生命周期參數(shù)標(biāo)記,實(shí)際上是和編譯器約定了一個規(guī)則:

結(jié)構(gòu)體實(shí)例的生命周期應(yīng)短于或等于任意一個成員的生命周期。

2.4.3.4 方法定義中的生命周期參數(shù)

結(jié)構(gòu)體中包含引用類型成員時,需要標(biāo)注生命周期參數(shù),則在impl關(guān)鍵字之后也需要聲明生命周期參數(shù),并在結(jié)構(gòu)體名稱之后使用。

例如:

impl<'a> Foo<'a> {
         fn split_first(s: &'a str) -> &'a str {
                   …
}
}

在添加生命周期參數(shù)'a之后,結(jié)束了輸入引用的生命周期長度要長于結(jié)構(gòu)體Foo實(shí)例的生命周期長度。

注:枚舉體和結(jié)構(gòu)體對生命周期參數(shù)的處理方式是一樣的。

2.4.3.5 靜態(tài)生命周期參數(shù)

靜態(tài)生命周期 'static:是Rust內(nèi)置的一種特殊的生命周期。'static生命周期存活于整個程序運(yùn)行期間。所有的字符串字面量都有生命周期,類型為& 'static str

字符串字面量是全局靜態(tài)類型,他的數(shù)據(jù)和程序代碼一起存儲在可執(zhí)行文件的數(shù)據(jù)段中,其地址在編譯期是已知的,并且是只讀的,無法更改。

如何掌握Rust語言

2.4.3.6 省略生命周期參數(shù)

滿足以下三條規(guī)則時,可以省略生命周期參數(shù)。該場景下,是將其硬編碼到Rust編譯器重,以便編譯期可以自動補(bǔ)齊函數(shù)簽名中的生命周期參數(shù)。

生命周期省略規(guī)則:

  • l 每一個在輸入位置省略的生命周期都將成為一個不同的生命周期參數(shù)。即對應(yīng)一個唯一的生命周期參數(shù)。

  • l 如果只有一個輸入的生命周期位置(無論省略還是沒省略),則該生命周期都將分配給輸出生命周期。

  • l 如果有多個輸入生命周期位置,而其中包含著 &self 或者 &mut self,那么 self 的生命周期都將分配給輸出生命周期。

以上這部分規(guī)則還沒理解透徹,需要繼續(xù)熟讀《Rust編程之道》的第143頁。

2.4.3.7 生命周期限定

生命周期參數(shù)可以向trait那樣作為泛型的限定,有以下兩種形式:

  • T: 'a,表示T類型中的任何引用都要“獲得”和'a一樣長。

  • T: Trait + 'a,表示T類型必須實(shí)現(xiàn)Trait這個trait,并且T類型中任何引用都要“活的”和'a一樣長。

具體的舉例見《Rust編程之道》的第145頁。

2.4.3.8 trait對象的生命周期

具體的舉例見《Rust編程之道》的第146頁。

2.4.3.9 高階生命周期

Rust還提供了高階生命周期(Higher-Ranked Lifetime)方案,該方案也叫高階trait限定(Higher-Ranked Trait Bound,HRTB)。該方案提供了for<>語法。

for<>語法整體表示此生命周期參數(shù)只針對其后面所跟著的“對象”。

具體的可見《Rust編程之道》的第192頁。

2.5 并發(fā)安全與所有權(quán)

2.5.1 標(biāo)簽trait:Send和Sync

? 如果類型T實(shí)現(xiàn)了Send: 就是告訴編譯器該類型的實(shí)例可以在線程間安全傳遞所有權(quán)

? 如果類型T實(shí)現(xiàn)了Sync:就是向編譯器表明該類型的實(shí)例在多線程并發(fā)中不可能導(dǎo)致內(nèi)存不安全,所以可以安全的跨線程共享。

2.5.2 哪些類型實(shí)現(xiàn)了Send

2.5.3 哪些類型實(shí)現(xiàn)了Sync

2.6 原生類型

Rust內(nèi)置的原生類型 (primitive types) 有以下幾類:

  • 布爾類型:有兩個值true和false。

  • 字符類型:表示單個Unicode字符,存儲為4個字節(jié)。

  • 數(shù)值類型:分為有符號整數(shù) (i8, i16, i32, i64, isize)、 無符號整數(shù) (u8, u16, u32, u64, usize) 以及浮點(diǎn)數(shù) (f32, f64)。

  • 字符串類型:最底層的是不定長類型str,更常用的是字符串切片&str和堆分配字符串String, 其中字符串切片是靜態(tài)分配的,有固定的大小,并且不可變,而堆分配字符串是可變的。

  • 數(shù)組:具有固定大小,并且元素都是同種類型,可表示為[T; N]。

  • 切片:引用一個數(shù)組的部分?jǐn)?shù)據(jù)并且不需要拷貝,可表示為&[T]。

  • 元組:具有固定大小的有序列表,每個元素都有自己的類型,通過解構(gòu)或者索引來獲得每個元素的值。

  • 指針:最底層的是裸指針const T和mut T,但解引用它們是不安全的,必須放到unsafe塊里。

  • 函數(shù):具有函數(shù)類型的變量實(shí)質(zhì)上是一個函數(shù)指針。

  • 元類型:即(),其唯一的值也是()。

2.7 函數(shù)

2.7.1 函數(shù)參數(shù)

  • l 當(dāng)函數(shù)參數(shù)按值傳遞時,會轉(zhuǎn)移所有權(quán)或者執(zhí)行復(fù)制(Copy)語義。

  • l 當(dāng)函數(shù)參數(shù)按引用傳遞時,所有權(quán)不會發(fā)生變化,但是需要有生命周期參數(shù)(符合規(guī)則時不需要顯示的標(biāo)明)。

2.7.2 函數(shù)參數(shù)模式匹配

  • ref :使用模式匹配來獲取參數(shù)的不可變引用。

  • ref mut :使用模式匹配來獲取參數(shù)的可變引用。

  • l 除了ref和ref mut,函數(shù)參數(shù)也可以使用通配符來忽略參數(shù)。

具體可見《Rust編程之道》的第165頁。

2.7.3 泛型函數(shù)

函數(shù)參數(shù)并未指定具體的類型,而是用了泛型T,對T只有一個Mult trait限定,即只有實(shí)現(xiàn)了Mul的類型才可以作為參數(shù),從而保證了類型安全。

泛型函數(shù)并未指定具體類型,而是靠編譯器來進(jìn)行自動推斷的。如果使用的都是基本原生類型,編譯器推斷起來比較簡單。如果編譯器無法自動推斷,就需要顯式的指定函數(shù)調(diào)用的類型。

2.7.4 方法和函數(shù)

方法代表某個實(shí)例對象的行為,函數(shù)只是一段簡單的代碼,它可以通過名字來進(jìn)行調(diào)用。方法也是通過名字來進(jìn)行調(diào)用,但它必須關(guān)聯(lián)一個方法接受者。

2.7.5 高階函數(shù)

高階函數(shù)是指以函數(shù)作為參數(shù)或返回值的函數(shù),它是函數(shù)式編程語言最基礎(chǔ)的特性。

具體可見《Rust編程之道》的第168頁。

2.8 閉包Closure

閉包通常是指詞法閉包,是一個持有外部環(huán)境變量的函數(shù)。

外部環(huán)境是指閉包定義時所在的詞法作用域。

外部環(huán)境變量,在函數(shù)式編程范式中也被稱為自由變量,是指并不是在閉包內(nèi)定義的變量。

將自由變量和自身綁定的函數(shù)就是閉包

閉包的大小在編譯期是未知的。

2.8.1 閉包的基本語法

閉包管道符(兩個對稱的豎線)和花括號(或圓括號)組成。

管道符里是閉包函數(shù)的參數(shù),可以向普通函數(shù)參數(shù)那樣在冒號后添加類型標(biāo)注,也可以省略。例如:let add = |a, b| -> i32 { a + b };

花括號里包含的是閉包函數(shù)執(zhí)行體,花括號和返回值也可以省略。

例如:let add = |a, b| a + b;

? 當(dāng)閉包函數(shù)沒有參數(shù)只有捕獲的自由變量時,管道符里的參數(shù)也可以省略。

例如: let add = || a + b;

2.8.2 閉包的實(shí)現(xiàn)

閉包是一種語法糖。閉包不屬于Rust語言提供的基本語法要素,而是在基本語法功能之上又提供的一層方便開發(fā)者編程的語法。

閉包和普通函數(shù)的差別就是閉包可以捕獲環(huán)境中的自由變量。

閉包可以作為函數(shù)參數(shù),這一點(diǎn)直接提升了Rust語言的抽象表達(dá)能力。當(dāng)它作為函數(shù)參數(shù)傳遞時,可以被用作泛型的trait限定,也可以直接作為trait對象來使用。

閉包無法直接作為函數(shù)的返回值,如果要把閉包作為返回值,必須使用trait對象。

2.8.3 閉包與所有權(quán)

閉包表達(dá)式會由編譯器自動翻譯為結(jié)構(gòu)體實(shí)例,并為其實(shí)現(xiàn)Fn、FnMut、FnOnce三個trait中的一個。

  • lFnOnce:會轉(zhuǎn)移方法接收者的所有權(quán)。沒有改變環(huán)境的能力,只能調(diào)用一次。

  • FnMut:會對方法接收者進(jìn)行可變借用。有改變環(huán)境的能力,可以多次調(diào)用。

  • Fn:會對方法接收者進(jìn)行不可變借用。沒有改變環(huán)境的能力,可以多次調(diào)用。

? 如果要實(shí)現(xiàn)Fn,就必須實(shí)現(xiàn)FnMutFnOnce;

? 如果要實(shí)現(xiàn)FnMut,就必須實(shí)現(xiàn)FnOnce;

? 如果要實(shí)現(xiàn)FnOnce,就不需要實(shí)現(xiàn)FnMutFn。

2.8.3.1 捕獲環(huán)境變量的方式

  • l 對于復(fù)制語義類型,以不可變引用(&T)來進(jìn)行捕獲。

  • l 對于移動語義類型,執(zhí)行移動語義,轉(zhuǎn)移所有權(quán)來進(jìn)行捕獲。

  • l 對于可變綁定,并且在閉包中包含對其進(jìn)行修改的操作,則以可變引用(&mut T)來進(jìn)行捕獲。

具體可見《Rust編程之道》的第178頁。

Rust使用move關(guān)鍵字來強(qiáng)制讓閉包所定義環(huán)境中的自由變量轉(zhuǎn)移到閉包中。

2.8.3.2 規(guī)則總結(jié)

  • l 如果閉包中沒有捕獲任何環(huán)境變量,則默認(rèn)自動實(shí)現(xiàn)Fn。

  • l 如果閉包中捕獲了復(fù)制語義類型的環(huán)境變量,則:

? 如果不需要修改環(huán)境變量,無論是否使用move關(guān)鍵字,均會自動實(shí)現(xiàn)Fn

? 如果需要修改環(huán)境變量,則自動實(shí)現(xiàn)FnMut。

  • l 如果閉包中捕獲了移動語義類型的環(huán)境變量,則:

? 如果不需要修改環(huán)境變量,而且沒有使用move關(guān)鍵字,則會自動實(shí)現(xiàn)FnOnce。

? 如果不需要修改環(huán)境變量,而且使用move關(guān)鍵字,則會自動實(shí)現(xiàn)Fn。

? 如果需要修改環(huán)境變量,則自動實(shí)現(xiàn)FnMut。

  • FnMut的閉包在使用move關(guān)鍵字時,如果捕獲變量是復(fù)制語義類型的,則閉包會自動實(shí)現(xiàn)Copy/Clone。如果捕獲變量是移動語義類型的,則閉包不會自動實(shí)現(xiàn)Copy/Clone。

2.9 迭代器

Rust使用的是外部迭代器,也就是for循環(huán)。外部迭代器:外部可以控制整個遍歷進(jìn)程。

Rust中使用了trait來抽象迭代器模式。Iterator trait是Rust中對迭代器模式的抽象接口。

迭代器主要包含:

  • next方法:迭代其內(nèi)部元素

  • 關(guān)聯(lián)類型Item

  • size_hint方法:返回類型是一個元組,該元組表示迭代器剩余長度的邊界信息。

示例:

  let iterator = iter.into_iter();      
let size_lin = iterator.size_hint(); 
 
let mut counter = Counter { count: 0};
counter.next();

Iter類型迭代器,next方法返回的是Option<&[T]>Option<&mut [T]>類型的值。for循環(huán)會自動調(diào)用迭代器的next方法。for循環(huán)中的循環(huán)變量則是通過模式匹配,從next返回的Option<&[T]>Option<&mut [T]>類型中獲取&[T]&mut [T]類型的值。

Iter類型迭代器在for循環(huán)中產(chǎn)生的循環(huán)變量為引用。

IntoIter類型的迭代器的next方法返回的是Option<T>類型,在for循環(huán)中產(chǎn)生的循環(huán)變量是,而不是引用。

示例:

let v = vec![1, 2, 3];for i in v { …}
let v = vec![1, 2, 3];for i in v { …}
let v = vec![1, 2, 3];for i in v { …}
let v = vec![1, 2, 3];for i in v { …}

為了確保size_hint方法可以獲得迭代器長度的準(zhǔn)確信息,Rust引入了兩個trait,他們是Iterator的子trait,均被定義在std::iter模塊中。

  • ExactSizeIterator :提供了兩個額外的方法lenis_empty。

  • TrustedLen :像一個標(biāo)簽trait,只要實(shí)現(xiàn)了TrustLen的迭代器,其size_hint獲取的長度信息均是可信的。完全避免了容器的容量檢查,提升了性能。

2.9.1 IntoIterator trait

如果想要迭代某個集合容器中的元素,必須將其轉(zhuǎn)換為迭代器才可以使用。

Rust提供了FromIterator和IntoIterator兩個trait,他們互為反操作。

  • FromIterator :可以從迭代器轉(zhuǎn)換為指定類型。

  • IntoIterator :可以從指定類型轉(zhuǎn)換為迭代器。

Intoiter可以使用into_iter之類的方法來獲取一個迭代器。into_iter的參數(shù)時self,代表該方法會轉(zhuǎn)移方法接收者的所有權(quán)。而還有其他兩個迭代器不用轉(zhuǎn)移所有權(quán)。具體的如下所示:

  • Intoiter :轉(zhuǎn)移所有權(quán),對應(yīng)self

  • Iter :獲取不可變借用,對應(yīng)&self

  • IterMut :獲得可變借用,對應(yīng)&mut slef

2.9.2 哪些實(shí)現(xiàn)了Iterator的類型?

只有實(shí)現(xiàn)了Iterator的類型才能作為迭代器。

實(shí)現(xiàn)了IntoIterator的集合容器可以通過into_iter方法來轉(zhuǎn)換為迭代器。

實(shí)現(xiàn)了IntoIterator的集合容器有:

  • lVec<T>

  • l&’a [T]

  • l&’a mut [T] => 沒有為[T]類型實(shí)現(xiàn)IntoIterator

  • l

2.9.3 迭代器適配器

通過適配器模式可以將一個接口轉(zhuǎn)換成所需要的另一個接口。適配器模式能夠使得接口不兼容的類型在一起工作。

適配器也叫包裝器(Wrapper)。

迭代器適配器,都定義在std::iter模塊中:

  • Map :通過對原始迭代器中的每個元素調(diào)用指定閉包來產(chǎn)生一個新的迭代器。

  • Chain :通過連接兩個迭代器來創(chuàng)建一個新的迭代器。

  • Cloned :通過拷貝原始迭代器中全部元素來創(chuàng)建新的迭代器。

  • Cycle :創(chuàng)建一個永遠(yuǎn)循環(huán)迭代的迭代器,當(dāng)?shù)戤吅?,再返回第一個元素開始迭代。

  • Enumerate :創(chuàng)建一個包含計數(shù)的迭代器,它返回一個元組(i,val),其中i是usize類型,為迭代的當(dāng)前索引,val是迭代器返回的值。

  • Filter :創(chuàng)建一個機(jī)遇謂詞判斷式過濾元素的迭代器。

  • FlatMap :創(chuàng)建一個類似Map的結(jié)構(gòu)的迭代器,但是其中不會包含任何嵌套。

  • FilterMap :相當(dāng)于Filter和Map兩個迭代器一次使用后的效果。

  • Fuse :創(chuàng)建一個可以快速遍歷的迭代器。在遍歷迭代器時,只要返回過一次None,那么之后所有的遍歷結(jié)果都為None。該迭代器適配器可以用于優(yōu)化。

  • Rev :創(chuàng)建一個可以反向遍歷的迭代器。

具體可見《Rust編程之道》的第202頁。

Rust可以自定義迭代器適配器,具體的見《Rust編程之道》的第211頁。

2.10 消費(fèi)器

迭代器不會自動發(fā)生遍歷行為,需要調(diào)用next方法去消費(fèi)其中的數(shù)據(jù)。最直接消費(fèi)迭代器數(shù)據(jù)的方法就是使用for循環(huán)。

Rust提供了for循環(huán)之外的用于消費(fèi)迭代器內(nèi)數(shù)據(jù)的方法,叫做消費(fèi)器(Consumer)

Rust標(biāo)準(zhǔn)庫std::iter::Iterator中常用的消費(fèi)器:

  • any :可以查找容器中是否存在滿足條件的元素。

  • fold :該方法接收兩個參數(shù),第一個為初始值,第二個為帶有兩個參數(shù)的閉包。其中閉包的第一個參數(shù)被稱為累加器,它會將閉包每次迭代執(zhí)行的結(jié)果進(jìn)行累計,并最終作為fold方法的返回值。

  • collect :專門用來將迭代器轉(zhuǎn)換為指定的集合類型。

  • lall

  • lfor_each

  • lposition

2.11 鎖

  • RwLock讀寫鎖:是多讀單寫鎖,也叫共享獨(dú)占鎖。它允許多個線程讀,單個線程寫。但是在寫的時候,只能有一個線程占有寫鎖;而在讀的時候,允許任意線程獲取讀鎖。讀鎖和寫鎖不能被同時獲取。

  • Mutex互斥鎖:只允許單個線程讀和寫。

三、 Rust屬性

#[lang = “drop”] : 將drop標(biāo)記為語言項(xiàng)

#[derive(Debug)] :

#[derive(Copy, Clone)] :

#[derive(Debug,Copy,Clone)] :

#[lang = “owned_box”] : Box<T>與原生類型不同,并不具備類型名稱,它代表所有權(quán)唯一的智能指針的特殊性,需要使用lang item來專門識別。

#[lang = “fn/fn_mut/fn_once”] :表示其屬于語言項(xiàng),分別以fn、fn_mut、fn_once名稱來查找這三個trait。

l fn_once:會轉(zhuǎn)移方法接收者的所有權(quán)

fn_mut:會對方法接收者進(jìn)行可變借用

fn:會對方法接收者進(jìn)行不可變借用

#[lang = “rust_pareen_sugar”] :表示對括號調(diào)用語法的特殊處理。

#[must_use=”iterator adaptors are lazy ……”] :用來發(fā)出警告,提示開發(fā)者迭代器適配器是惰性的。

四、內(nèi)存管理

4.1 內(nèi)存回收

drop-flag:在函數(shù)調(diào)用棧中為離開作用域的變量自動插入布爾標(biāo)記,標(biāo)注是否調(diào)用析構(gòu)函數(shù),這樣,在運(yùn)行時就可以根據(jù)編譯期做的標(biāo)記來調(diào)用析構(gòu)函數(shù)。

實(shí)現(xiàn)了Copy的類型,是沒有析構(gòu)函數(shù)的。因?yàn)閷?shí)現(xiàn)了Copy的類型會復(fù)制,其生命周期不受析構(gòu)函數(shù)的影響。

需要繼續(xù)深入理解第4章并總結(jié),待后續(xù)補(bǔ)充。

五、unicode

Unicode字符集相當(dāng)于一張表,每個字符對應(yīng)一個非負(fù)整數(shù),該數(shù)字稱為碼點(diǎn)(Code Point)

這些碼點(diǎn)也分為不同的類型:

  • l 標(biāo)量值

  • l 代理對碼點(diǎn)

  • l 非字符碼點(diǎn)

  • l 保留碼點(diǎn)

  • l 私有碼點(diǎn)

標(biāo)量值是指實(shí)際存在對應(yīng)字符的碼位,其范圍是0x0000~0xD7FF0xE000~0x10FFFF兩段。

Unicode字符集的每個字符占4個字節(jié),使用的存儲方式是:碼元(Code Unit)組成的序列。

碼元是指用于處理和交換編碼文本的最小比特組合。

Unicode字符編碼表:

  • UTF-8 => 1字節(jié)碼元

  • UTF-16 => 2字節(jié)碼元

  • UTF-32 => 4字節(jié)碼元

Rust的源碼文件.rs的默認(rèn)文本編碼格式是UTF-8。

“如何掌握Rust語言”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識可以關(guān)注億速云網(wǎng)站,小編將為大家輸出更多高質(zhì)量的實(shí)用文章!

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI