目前僅看了第二版的官方文檔,記錄一下初步印象,應該還有更深入一致的解釋,水平有限,僅供參考。
實驗環境:ubuntu17.10,rust1.18,vscode1.14 + 擴展rust(rls)。
BTW,環境搭建順利得使人意外,Rust工具鏈打造的簡潔精美,原生支持git,安裝只需一條命令:curl https://sh.rustup.rs -sSf | sh
。git
數據競爭主要有三個條件:github
Rust很是重視併發,根據官方介紹:Rust 是一門着眼於安全、速度和併發的編程語言。而併發須要解決的就是數據競爭問題,天然會很是重視數據的使用過程,說是當心翼翼不爲過。由於數據要關聯到有名變量才能使用,因此rust在語言層面上針對變量的使用引入瞭解決方法,主要涉及的語法有:編程
須要注意的是,全部權僅針對複雜類型變量(在語法上,是沒有copy trait的類型),例如String、vect等在堆上存儲數據的類型,而簡單類型並不用考慮,如int、tuple、array等,緣由就在於賦值時數據是如何拷貝的(雖然都是淺拷貝)。ubuntu
若是熟悉淺拷貝、深拷貝的概念,天然瞭解,對於在堆上分配空間的複雜類型,淺拷貝會致使兩個或更多變量/指針同時指向同⼀數據,如有變量/指針做寫入操做,就會引發數據競爭問題。
安全
因此,Rust用可變/不可變、全部權、生命期等來破壞數據競爭的條件,而這些解決方案所有在編譯期搞定!
固然,代價是難以快速驗證想法,畢竟使用變量時要仔細了,不然編都編不過,期待最佳實踐和IDE的支持。數據結構
let x = 3; // x 默認不可變 x = 4; // 錯誤! let x = 4; // 正確!遮蓋了原有的同名變量 let mut y = 3; // y可變 y = 4; // 正確!
fn test(v: String) { println!("fn: {}", v); } // 函數 let x = String::from("hello"); // 全部者x(String類型) let y = x; // 數據的全部權轉移給y! let z = x; // 錯誤!x已不可用 test(y); // 全部權轉移,新的全部者是形參v!當函數執行完畢,v離開做用域時值被丟棄(drop)! println!("var: {}", y); // 錯誤!y已不可用
這不免有使人抓狂的感受,還能不能愉快地玩耍了?這數據跑得跟兔子同樣,想用的時候都不知道去哪了!還可能無心中跑到函數裏直接躺屍!併發
那麼,一個變量想屢次使用怎麼辦?答案是能夠借用**:使⽤其值但不獲取其全部權**。curl
fn test1(v: String) { println!("fn: {}", v); } fn test2(v: &String) { println!("fn: {}", v); } // 參數爲引用類型 let s = String::from("hello"); // 全部者s(String類型) let s1 = &s; // 不可變借用(borrow)! let s2 = &s; // 借用 let s3 = s1; // 借用 test2(s1); // 借用 test1(*s1); // 錯誤!借用者s1沒有全部權,沒法經過s1轉移(cannot move out of borrowed content)。 println!("var: {}", s); // 正確
**小結:**我的感受,全部權轉移主要爲併發服務,自己並不經常使用,畢竟數據常常要複用,沒人樂意要一直提防着數據跑哪去了,尤爲在函數調用時。既然如此,通常把全部者保持不變,多使用引用,主要體如今複雜數據結構和函數上。編程語言
可是,實際使用的狀況會比較複雜,即是否可變與轉移、借用三者相互影響(混用)的狀況。
從數據競爭的角度:讀讀不衝突,但讀寫、寫寫會衝突(讀即不可變,寫便可變);從實現的角度:引用是基於全部權的。
所以,能夠看看哪些對象會衝突:(全部者,引用) × (不可變,可變) 函數
let x = String::from("hello"); let mut z = x; // 轉移後變量x不可用 z.push_str(" z"); //正確 // 可變引用要用星號來得到引用的內容,不可變引用不須要。 let mut x = 5; let y = &mut x; *y += 1;
let mut x = String::from("hello"); let y = &x; // 不可變引用 let z = x; // 錯誤 x.push_str(" x"); // 錯誤 let z = &mut x; // 錯誤:可變引用
可變引用使用上略複雜,概念上也沒有太統一的理解,這裏單獨考查。
「可變權」便可變引用對數據的讀寫權,具備惟一性(只有一個可用的可變引用)和獨佔性(其它讀、寫通通無效),因此對編譯影響至關大。可變引用的可變權和全部者對數據的全部權有類似性,由於可變權也有move行爲。
**注:**官方文檔裏沒有可變權的概念,但我的感受,用這個概念比較好理解可變引用的使用,也許還有更本質的解釋,僅供參考。
let mut x = String::from("hello"); // 全部者x有可變權 // 1. 直接轉移 let y = &mut x; // 1. y爲可變引用,可變權move自x let z = y; // 直接轉移。z爲可變引用 y.push_str(" y"); // 錯誤!y的可變權已move給z z.push_str(" z"); // 正確 // 2. 間接轉移 let mut y = &mut x; // 2. y爲可變引用,可變權move自x let w = &mut y; // 要求y可變。w爲可變引用 w.push_str(" w"); // 正確 // 轉移(函數) fn test(i: &mut String) { i.push_str(" i"); // 正確 } let mut x = String::from("hello"); // 全部者x有可變權 test(&mut x); x.push_str(" x"); // 正確!可變權已歸還
let x = String::from("hello"); // x不可變 let mut z = &x; // z爲不可變引用 z.push_str(" z"); // 錯誤! let w = &mut z; // w爲可變引用 w.push_str(" w"); // 錯誤! let mut y = x; // 全部權轉移,y可變 let z = &mut y; // z爲可變引用,要求y可變 z.push_str(" z"); // 正確! let w = &z; // w 爲不可變引用 w.push_str(" w"); // 錯誤!
由於都涉及到值的修改,可變引用的行爲和全部者類似,並且可變權和全部權都是面向數據且惟一的。
全部者
- 有全部權,move後再也不可用,當全部者生命期結束,值被丟棄。
- 讀的時候相似不可變引用,寫的時候相似可變引用。
可變引用(&mut T)
- 有可變權,move自被引用者,當可變引用生命期結束,可變權自動歸還。
- 可變權的源頭應該來自全部者,不然意義不大。