初識 Rust 語言的全部權概念

目前僅看了第二版的官方文檔,記錄一下初步印象,應該還有更深入一致的解釋,水平有限,僅供參考。
實驗環境:ubuntu17.10,rust1.18,vscode1.14 + 擴展rust(rls)。
BTW,環境搭建順利得使人意外,Rust工具鏈打造的簡潔精美,原生支持git,安裝只需一條命令:curl https://sh.rustup.rs -sSf | shgit

初步印象

數據競爭主要有三個條件:github

  1. 兩個或更多指針同時訪問同⼀數據。
  2. ⾄少有⼀個指針被寫⼊。
  3. 沒有同步數據訪問的機制。

Rust很是重視併發,根據官方介紹:Rust 是一門着眼於安全、速度和併發的編程語言。而併發須要解決的就是數據競爭問題,天然會很是重視數據的使用過程,說是當心翼翼不爲過。由於數據要關聯到有名變量才能使用,因此rust在語言層面上針對變量的使用引入瞭解決方法,主要涉及的語法有:編程

  1. 變量定義時,不可變(immutable,默認)、可變(mutable)
  2. 變量賦值時,全部權轉移(move)、借用(borrow)

須要注意的是,全部權僅針對複雜類型變量(在語法上,是沒有copy trait的類型),例如String、vect等在堆上存儲數據的類型,而簡單類型並不用考慮,如int、tuple、array等,緣由就在於賦值時數據是如何拷貝的(雖然都是淺拷貝)。ubuntu

若是熟悉淺拷貝、深拷貝的概念,天然瞭解,對於在堆上分配空間的複雜類型,淺拷貝會致使兩個或更多變量/指針同時指向同⼀數據,如有變量/指針做寫入操做,就會引發數據競爭問題。
複雜類型安全

因此,Rust用可變/不可變、全部權、生命期等來破壞數據競爭的條件,而這些解決方案所有在編譯期搞定!
固然,代價是難以快速驗證想法,畢竟使用變量時要仔細了,不然編都編不過,期待最佳實踐和IDE的支持。數據結構

基本概念

1. 不可變、可變

let x = 3;  // x 默認不可變
x = 4;  // 錯誤!
let x = 4;  // 正確!遮蓋了原有的同名變量
let mut y = 3;  // y可變
y = 4;  // 正確!

2. 全部權轉移(move)

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已不可用

這不免有使人抓狂的感受,還能不能愉快地玩耍了?這數據跑得跟兔子同樣,想用的時候都不知道去哪了!還可能無心中跑到函數裏直接躺屍!併發

3. 借用/引用(borrow)

那麼,一個變量想屢次使用怎麼辦?答案是能夠借用**:使⽤其值但不獲取其全部權**。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;
  • 雖然不可變引用(&T)沒有全部權,不會致使值被誤轉移,但借用之時要求值不能變,這意味着此時:全部權不能轉移、全部者不能改值、不能同時有可變引用!
let mut x = String::from("hello");
let y = &x;  // 不可變引用
let z = x;  // 錯誤
x.push_str(" x");  // 錯誤
let z = &mut x;  // 錯誤:可變引用

可變引用(&mut T)

可變引用使用上略複雜,概念上也沒有太統一的理解,這裏單獨考查。
「可變權」便可變引用對數據的讀寫權,具備惟一性(只有一個可用的可變引用)和獨佔性(其它讀、寫通通無效),因此對編譯影響至關大。可變引用的可變權和全部者對數據的全部權有類似性,由於可變權也有move行爲。

**注:**官方文檔裏沒有可變權的概念,但我的感受,用這個概念比較好理解可變引用的使用,也許還有更本質的解釋,僅供參考。

  • 可變權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");  // 錯誤!

總結:

由於都涉及到值的修改,可變引用的行爲和全部者類似,並且可變權和全部權都是面向數據且惟一的。

全部者

  1. 有全部權,move後再也不可用,當全部者生命期結束,值被丟棄。
  2. 讀的時候相似不可變引用,寫的時候相似可變引用。

可變引用(&mut T)

  1. 有可變權,move自被引用者,當可變引用生命期結束,可變權自動歸還。
  2. 可變權的源頭應該來自全部者,不然意義不大。

參考

  1. Rust 環境配置事項一覽
  2. 官方文檔:Rust 程序設計語言(第二版)
  3. Rust教程11之全部權
  4. 你在開發過程當中都遇到過 Rust 的哪些坑?
相關文章
相關標籤/搜索