傳送門:
深入淺出Rust(第一部分-1)
深入淺出Rust(第一部分-2)
深入淺出Rust(第二部分-1)
深入淺出Rust(第二部分-2)
深入淺出Rust(第三部分-1)
深入淺出Rust(第三部分-2)
深入淺出Rust(第四部分)
深入淺出Rust(第五部分)
第三部分 - 高級抽象 -1
第21章 泛型
看了引入泛型,就要考慮的方方面面,怪不得Go遲遲拿不出方案了...
Rust的泛型和java的不同,java只是在編譯器進行檢查,運行是進行類型擦除.而Rust是在編譯器時進行檢查和類型綁定.
1.數據結構中的泛型
- Option類型是一個泛型enum類型
struct S<T=i32>{
data: T
}
- 泛型參數T,使用時候可以不指定類型,這樣就用了默認值i32.
2. 函數中的泛型
- 在方法名后面加上<>泛型參數(與java是一樣)
- 手動指定參數: function_name::<type params>(function params)語法,這里用::分隔
- 泛型函數很多程度實現了C++的"函數重載"功能,通過對參數的泛化,是的參數能接受多種類型
3. impl塊中的泛型
- impl塊中的泛型: 直接再impl后面<>,并且配合where子句
4. 泛型參數約定
- Rust在分析泛型函數的時候當場檢查類型的合法性
21-1.png
5. 關聯類型(難點)
- 在定義trait時候還同時定義type,使得在impl時候,需要同時指定該類型.
- 也就是說trait中的泛型,可以不放第一句,而是單獨說明.
pub trait Iterator{
type Item;
}
- 增加可讀性,可擴展性;(不用對trait的每個函數單獨指定約束)
- trait的impl匹配規則,可以針對不同類型實現多個impl,而不會沖突.
6. 使用關聯類型(難點)
7. 泛型特化
- 僅僅針對trait和impl支持特化功能,當有多個impl可用時,編譯器很聰明會找到最特化的impl
- 特化意義: 性能優化,代碼重用,為"高效繼承"鋪路
- 使用default關鍵字,是的impl方法能夠被"重寫",從而完成特化
- 交叉impl---試驗性,并不完美
第22章 閉包
閱讀下來,語義上閉包和js的閉包區別不大,語法小有區別
語法:
|a: i32, b: i32| -> i32 { return a+b;}
簡寫:
|a, b| -> a+b;
省略類型,{},return,這些和java,js倒是一樣的.
1. 變量捕獲
- 閉包屬于語法糖,Rust中是通過匿名結構體實現的,它會捕獲用到的外部變量,傳入內部
- 優先選擇&T類型,其次&mut T,最后T類型.(我都要的策略)
2. move關鍵字
- 加上move關鍵字,編譯器生成的匿名結構體都使用by value方式(傳入T類型)
- 用于閉包需要傳遞到函數外部的情況
3. Fn/FnMut/FnOnce
- 閉包還自動實現了幾個trait
- FnOnce:傳入self,執行一次后失效
- FnMut: 傳入&mut self,能改變外部環境變量和自己成員變量
- Fn: 傳入&self,只能改變外部環境變量
4. 閉包和泛型(難)
- 要向函數傳遞閉包(1. 不同參數生成不同版本函數:靜態; 2. 進行trait object裝箱: 動態)
5. 閉包與生命周期(難)
- 要讓閉包作為返回值,生命周期標記控制比較困難->高階生命周期
- .....再補吧.
第23章 動態分派和靜態分派(難)
- Rust可以同時支持靜態分派(static dispatch)和動態分派(dynamic dispatch)
- 所謂動態,指在運行時才知道具體調用哪個函數(函數名是知道的,類型未知)
- 引入dyn關鍵字
23-1.png
1. trait object
- 指向triat的指針(類似于Go的接口指針),其為一個DST動態大小類型
- 因此變成"胖指針",同時包含數據地址(data)和虛函數表(vtable),而虛函數表包含我們具體需要調用函數的地址
2. object safe(需要額外限制,保證trait object的安全)
- trait有Self:Sized約束時,不允許
- 函數中有Self類型作為參數或者返回類型時,不允許(不能出現在虛函數表)
- 當函數第一個參數不是self時,不允許(加不到虛函數表)
- 當函數有泛型參數時,不允許(可能出現不同版本,沖突)
3. impl trait(略)
- .....再補吧.