[Rust] 重點摘要

程式語言:Rust
官網
Rust Playground(線上撰寫)

簡介:Rust 的特色介紹

學習資源
變數可被遮蔽
線上測試範例
  1. fn main() {
  2. let x = 5;
  3. println!("x 的數值為:{}", x);
  4. let x = "five";
  5. println!("x 的數值為:{}", x);
  6. }
變數宣告
使用前必須有值
線上測試範例
  1. fn main() {
  2. // // type annotations needed
  3. let x;
  4. println!("x 的數值為:{}", x);
  5. // borrow of possibly-uninitialized variable: `x`
  6. let x;
  7. println!("x 的數值為:{}", x);
  8. x=2;
  9. // OK
  10. let x;
  11. x=2;
  12. println!("x 的數值為:{}", x);
  13. }
宣告時,需指定變數是否可變
線上測試範例
  1. fn main() {
  2. // 不可變
  3. let x = 5;
  4. println!("x 的數值為:{}", x);
  5. // cannot assign twice to immutable variable `x`
  6. x = 6;
  7. println!("x 的數值為:{}", x);
  8. // 可變
  9. let mut x = 5;
  10. println!("x 的數值為:{}", x);
  11. x = 6;
  12. println!("x 的數值為:{}", x);
  13. }
  • Rust 中每個數值都會有一個變數作為它的擁有者(owner)。
  • 同時間只能有一個擁有者。
  • 當擁有者離開作用域時,數值就會被丟棄。
若資料在堆積(Heap)上,Rust 永遠不會自動將你的資料建立「深拷貝」。
因此任何自動的拷貝動作都可以被視為是對執行效能影響很小的,但可手動 clone
若只在堆疊(Stack)上的資料,會自動拷貝,因擁有 Copy Trait,如下型別
  • 所有整數型別像是 u32。
  • 布林型別 bool,它只有數值 true 與 false。
  • 所有浮點數型別像是 f64。
  • 字元型別 char。
  • 元組,不過包含的型別也都要有 Copy 才行。比如 (i32, i32) 就有 Copy,但 (i32, String) 則無。
線上測試範例
  1. fn main() {
  2. {
  3. // s 在此處無效,因為它還沒宣告
  4. let s = "hello"; // s 在此開始視為有效
  5.  
  6. println!("{}", s); // 使用 s
  7. } // 此作用域結束, s 不再有效
  8. // cannot find value `s` in this scope
  9. println!("{}", s); // 會出錯
  10. //=========================================================
  11. let s1 = String::from("hello");
  12. let s2 = s1;
  13. // borrow of moved value: `s1`
  14. println!("{}", s1); // 會出錯
  15. //=========================================================
  16. let x = 5;
  17. let y = x;
  18. println!("{}", x); // 不會出錯,因為 Copy Trait
  19. //=========================================================
  20. let s1 = String::from("hello");
  21. let s2 = s1.clone();
  22. println!("s1 = {}, s2 = {}", s1, s2); // 不會出錯,因重新 clone 一份給 s2
  23. }
基本用法,可用引用閃過所有權
線上測試範例
  1. fn main() {
  2. let s1 = String::from("hello");
  3. let s2 = &s1;
  4. println!("{} {}", s1, s2); // 不會出錯,因 s2 為引用
  5. //cannot borrow `*s2` as mutable, as it is behind a `&` reference
  6. // s2.push_str(", world"); // 因 s2 為不可變引用,故會出錯
  7. // println!("{}", s2);
  8. let mut s3 = String::from("hello");
  9. let s4 = &mut s3;
  10. s4.push_str(", world");
  11. println!("{}", s4);
  12. // cannot borrow `s1` as mutable, as it is not declared as mutable
  13. let s5 = &mut s1;
  14. s5.push_str(", world");
  15. println!("{}", s5);
  16. }
限制條件
  • 在任何時候,我們要嘛只能有一個可變引用,要嘛可以有任意數量的不可變引用。
  • 引用必須永遠有效。
這項限制的好處是 Rust 可以在編譯時期就防止資料競爭(data races)。資料競爭和競爭條件(race condition)類似,它會由以下三種行為引發:
  • 同時有兩個以上的指標存取同個資料。
  • 至少有一個指標在寫入資料。
  • 沒有針對資料的同步存取機制。
線上測試範例
  1. fn main() {
  2. let mut s = String::from("hello");
  3.  
  4. let r1 = &mut s;
  5. // cannot borrow `s` as mutable more than once at a time
  6. let r2 = &mut s;
  7. println!("{}, {}", r1, r2);
  8. //===================================================
  9. let mut s = String::from("hello");
  10. {
  11. let r1 = &mut s;
  12. } // r1 離開作用域,所以建立新的引用也不會有問題
  13. let r2 = &mut s;
  14. //===================================================
  15. let mut s = String::from("hello");
  16. let r1 = &s; // 沒問題
  17. let r2 = &s; // 沒問題
  18. // cannot borrow `s` as mutable because it is also borrowed as immutable
  19. let r3 = &mut s; // 很有問題!
  20. println!("{}, {}, and {}", r1, r2, r3);
  21. //===================================================
  22. let mut s = String::from("hello");
  23. let r1 = &s; // 沒問題
  24. let r2 = &s; // 沒問題
  25. println!("{} and {}", r1, r2);
  26. // r1 和 r2 從此不再使用
  27. let r3 = &mut s; // 沒問題
  28. println!("{}", r3);
  29. //===================================================
  30. {
  31. let mut s = String::from("hello");
  32. } // s 離開作用域,已被 drop
  33. // cannot find value `s` in this scope
  34. let r1 = &mut s; // 很有問題!
  35. }
線上測試範例
  1. #![allow(unused)]
  2. fn main() {
  3. use std::fs::File;
  4. use std::io;
  5. use std::io::Read;
  6.  
  7. fn read_username_from_file() -> Result<String, io::Error> {
  8. let f = File::open("hello.txt");
  9.  
  10. let mut f = match f {
  11. Ok(file) => file,
  12. Err(e) => return Err(e),
  13. };
  14.  
  15. let mut s = String::new();
  16.  
  17. match f.read_to_string(&mut s) {
  18. Ok(_) => Ok(s),
  19. Err(e) => Err(e),
  20. }
  21. }
  22. // 利用 ? 可達到同上的效果 ===========================
  23.  
  24. fn read_username_from_file() -> Result<String, io::Error> {
  25. let mut f = File::open("hello.txt")?;
  26. let mut s = String::new();
  27. f.read_to_string(&mut s)?;
  28. Ok(s)
  29. }
  30. }
可利用特徵界限,來限定型別
  1. // 以下兩者等同
  2. fn trait_fn<T: Copy + Debug>(a: T) -> T {}
  3. fn trait_where<T>(a: T) -> T
  4. where
  5. T: Copy + Debug,
  6. {
  7. }
  8.  
  9. // 以下兩者等同
  10. fn trait_iml(a: impl Copy) -> impl Copy {}
  11. fn trait_iml<T: Copy>(a: T) -> impl Copy {}
  12.  
  13. // impl Trait 語法糖,只在輸入參數,回傳型別並不相同
  14. // 故以下兩者不等同
  15. fn trait_iml(a: impl Copy) -> impl Copy {}
  16. fn trait_iml<T: Copy>(a: T) -> T {}
生命週期詮釋不會改變引用能存活多久。
就像當函式簽名指定了一個泛型型別參數時,函式便能夠接受任意型別一樣。
函式可以指定一個泛型生命週期參數,這樣函式就能接受任何生命週期。
生命週期詮釋描述了數個引用的生命週期之間互相的關係,而不會影響其生命週期。
當引用沒有顯式詮釋生命週期時,編譯器會用三項規則來推導它們。
  • 第一個規則是每個引用都會有自己的生命週期參數。換句話說,一個函式只有一個參數的話,就只會有一個生命週期:
    fn foo<'a>(x: &'a i32);
    一個函式有兩個參數的話,就會有分別兩個生命週期參數:
    fn foo<'a, 'b>(x: &'a i32, y: &'b i32)
    ,以此類推。
  • 第二個規則是如果剛好只有一個輸入生命週期參數,該參數就會賦值給所有輸出生命週期參數:
    fn foo<'a>(x: &'a i32) -> &'a i32。
  • 第三個規則是如果有多個輸入生命週期參數,但其中一個是 &self 或 &mut self,由於這是方法,self 的生命週期會賦值給所有輸出生命週期參數。此規則讓方法更容易讀寫,因為不用寫更多符號出來。
線上測試範例
  1. fn main() {
  2. let string1 = String::from("abcd");
  3. let string2 = "xyz";
  4.  
  5. let result = longest(string1.as_str(), string2);
  6. println!("最長的字串為 {}", result);
  7. }
  8.  
  9. fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
  10. if x.len() > y.len() {
  11. x
  12. } else {
  13. y
  14. }
  15. }
let 即是一種 模式配對
  1. struct Point {
  2. x: i32,
  3. y: i32,
  4. }
  5.  
  6. fn main() {
  7. let p = Point { x: 0, y: 7 };
  8.  
  9. let Point { x: a, y: b } = p;
  10. assert_eq!(0, a);
  11. assert_eq!(7, b);
  12. }
線上測試範例
  1. enum Color {
  2. Rgb(i32, i32, i32),
  3. Hsv(i32, i32, i32),
  4. }
  5.  
  6. enum Message {
  7. Quit,
  8. Move { x: i32, y: i32 },
  9. Write(String),
  10. ChangeColor(Color),
  11. }
  12.  
  13. fn main() {
  14. let msg = Message::ChangeColor(Color::Hsv(0, 160, 255));
  15.  
  16. match msg {
  17. // 透過 .. 忽略剩餘部分數值
  18. Message::ChangeColor(Color::Rgb(..,b)) => println!(
  19. "藍色 {}",
  20. b
  21. ),
  22. // 透過 _ 忽略整個數值
  23. Message::ChangeColor(Color::Hsv(_, s, _)) => println!(
  24. "飽和度 {}",
  25. s
  26. ),
  27. // Guards,compiler 無法檢查 if 所有可能性,需搭配 _ =>
  28. Message::Move{y,..} if y > 0 => println!(
  29. "移動 y:{}",
  30. y
  31. ),
  32. // @ 綁定
  33. Message::Move{x: x_variable @ 3..=7, y:_ } => println!(
  34. "移動 x:{} 在 3~7",
  35. x_variable
  36. ),
  37. // 透過 ..= 配對數值範圍 只允許使用數字或 char 數值
  38. Message::Move{x: 3..=7, y:_ } => println!(
  39. "移動 x 在 3~7"
  40. ),
  41. // 多重模式
  42. Message::Quit | Message::Write(_)=> println!(
  43. "Quit or Write"
  44. ),
  45. _ => (),
  46. }
  47. }
詳細請參考連結
  1. #[no_mangle]
  2. pub extern "C" fn addition(a: u32, b: u32) -> u32 {
  3. a + b
  4. }

留言