50 個 Rust 新手常犯的錯誤:看看你中過幾條?

  1. 錯誤地使用可變和不可變借用
let mut data = vec![1, 2, 3];
let x = &data[0];
data.push(4);
println!("{}", x);

不能在有不可變引用時修改數據。

  1. 忘記處理 Option
fn main() {
    let some_number = Some(5);
    let sum = some_number + 5; // 錯誤:Option 類型不能這樣直接使用
}

使用 match 或 if let 來處理 Option。

let sum = if let Some(n) = some_number { n + 5 } else { 0 };
  1. 期望的引用而不是實際值
fn foo(x: &i32) {
    // ...
}

fn main() {
    let x = 10;
    foo(x); // 錯誤:應傳遞引用而不是值
}

傳遞引用而不是值。

foo(&x);
  1. 不匹配的類型
fn main() {
    let flag = true;
    let number = if flag { 5 } else { "six" }; // 錯誤:不匹配的類型
}

解決方案:確保所有的分支返回相同的類型。

let number = if flag { 5 } else { 6 };
  1. 忘記導入 trait
use std::io::Write; // 因爲 Write trait 已導入

fn main() {
    let mut buffer = Vec::new();
    buffer.write_all(b"hello").unwrap();
    // 現在可以正常使用 write_all 方法,因爲 Write trait 已導入
}

必須導入 Write trait 才能使用其方法。

  1. 試圖在遍歷向量的同時修改它
fn main() {
    let mut vec = vec![1, 2, 3];
    for val in &vec {
        vec.push(*val + 10); // 錯誤!不能在遍歷時修改vec
    }
}

修復: 使用迭代器的. for_each() 方法或者先收集需要做的更改,然後再應用它們。

  1. 不使用可變引用來修改向量中的值
fn main() {
    let mut vec = vec![1, 2, 3];
    for val in vec.iter() {
        *val *= 2; // 錯誤!因爲`val`是不可變引用
    }
}

修復: 使用. iter_mut() 來獲取可變引用。

for val in vec.iter_mut() {
    *val *= 2;
}
  1. 在向量迭代時錯誤地使用索引
fn main() {
    let vec = vec![10, 20, 30];
    for i in 0..vec.len() {
        println!("{}", vec[i]); // 索引方式正確
        // 如果修改爲 vec[i+1] 就會出錯
    }
}

修復: 確保在使用索引時不要越界。

  1. 未處理 get() 返回 Option 的情況
fn main() {
    let vec = vec![1, 2, 3];
    let val = vec.get(5); // 返回None,因爲索引超出範圍
    println!("{}", val.unwrap()); // 錯誤!這會panic
}

修復: 使用. unwrap_or() 或. unwrap_or_else() 處理 None 情況。

let val = vec.get(5).unwrap_or(&0); // 使用默認值
println!("{}", val);
  1. 使用不恰當的迭代器消費方法
fn main() {
    let vec = vec![1, 2, 3, 4, 5];
    let even_numbers: Vec<_> = vec.iter().map(|x| x * 2).collect(); // 正確
    let even_number = even_numbers.first().unwrap(); // 嘗試獲取第一個數
    println!("{}", even_number * 2); // 錯誤!even_number的類型是&&i32
}

修復: 在使用值之前進行適當的解引用。

println!("{}"(**even_number) * 2); // 解引用兩次到i32
  1. 在遍歷過程中錯誤地使用 for 循環去顯式創建迭代器
fn main() {
    let vec = vec![1, 2, 3];
    for i in 0..vec.iter().count() { // 錯誤!不必要的計數和索引
        println!("{}", vec[i]);
    }
}

修復: 直接遍歷迭代器。

for &val in vec.iter() {
    println!("{}", val);
}
  1. 忘記引用計數(Rc/Arc)中的借用規則
use std::rc::Rc;
let foo = Rc::new(5);
let a = *foo;

使用 * foo 應該先調用. clone() 去獲取 Rc 裏面的值。

  1. 錯誤地對字符串切片
let s = String::from("hello");
let part = &s[0..2];

字符串索引應該在字符邊界處切片。使用. chars() 和. bytes()。

  1. 未處理泛型邊界
fn print_it<T>(value: T) {
    println!("{}", value);
}

需要爲 T 添加 Display trait 約束。

  1. 嘗試修改迭代器產生的不可變引用
let mut vector = vec![1, 2, 3];
for item in vector.iter_mut() {
    *item += 10;
}

使用 iter_mut() 來獲取可變引用。

  1. 沒有正確使用借用和所有權
fn use_data(data: &Vec<i32>) {
    // do something
}
fn main() {
    let data = vec![1, 2, 3];
    use_data(&data);  // Correct
    use_data(data);   // Incorrect
}

保持對參數的引用和傳遞一致。

  1. 在 match 表達式內使用 = 而不是 if 來匹配守衛
let num = 10;
match num {
    x if x = 5 => println!("Five"),
    _ => println!("Not five"),
}

守衛使用 == 來進行比較。

  1. 枚舉匹配不全
enum Direction {
    Up,
    Down,
    Left,
    Right,
}
fn match_direction(dir: Direction) {
    match dir {
        Direction::Up => println!("Going up!"),
        // 缺少其他方向的匹配
    }
}

應該匹配枚舉的所有變體。

  1. 在非異步函數中使用 await
fn main() {
    let future = async_operation();
    let result = future.await;  // Error: `await` is only valid in async function
}

必須在 async 函數中使用 await。

  1. 將 & str 推入 String 而不使用 push_str 或 + 操作符
let mut hello = String::from("Hello, ");
hello.push("world!");  // Error: `push` takes a single character

使用 push_str 或 + 來連接字符串。

  1. rust 語言的不變性
let x = 5;
let y = &x;
let z = &mut x;  // Error: Cannot borrow `x` as mutable because it is also borrowed as immutable

不允許在不可變引用存在時創建可變引用。

  1. 沒有標記可變的閉包變量
let mut count = 0;
let mut inc = || {
    count += 1;  // Error: Cannot borrow immutable local variable `count` as mutable
};
inc();

閉包外的變量需要被標記爲 mut。

  1. 錯誤地使用結構體更新語法
struct Point {
    x: i32,
    y: i32,
}

let p1 = Point { x: 1, y: 2 };
let p2 = Point { y: 3, ..p1 };  // Error: `p1` used after move

p1 在更新之後不能被再次使用,除非字段是類型實現了 Copy trait。

  1. 在包含引用的結構體中提供顯式生命週期
struct Student<'a> {
    name: &'a str,
    age: u8,
}

let student_name = String::from("John");
let student = Student {
    name: &student_name,
    age: 20,
};

當結構體包含引用時,需要指定生命週期參數。

  1. 沒有意識到函數調用會獲取所有權
let mut v = vec![1, 2, 3];
drop(v);
v.push(4); // Error: Use of moved value `v`

使用 drop 後,原值將不再有效。

  1. 交替使用 &ref
let mut x = 10;
let y = &x;
let ref z = x;

使用 ref 在模式匹配中創建引用,& 用於創建引用。

  1. 忘記在使用線程時處理潛在的失敗情況
use std::thread;

let handle = thread::spawn(|| {
    panic!("oh no!");
});

handle.join(); // Error: Ignored `Result` which may indicate the thread has panicked

使用. join().unwrap() 來處理線程中可能發生的錯誤。

  1. 錯誤的轉型
let x: i32 = 10;
let y: u64 = x as u64; // Correct
let z: f64 = x.into(); // Error: A type annotation is required

選擇合適的類型轉換方法,確保類型實現了相應的轉換 trait。

  1. 使用 unwrap_or_else 時搭配非閉包參數
let result = Some(2);
let number = result.unwrap_or_else(0); // Error: Expected a closure that returns a value

使用 unwrap_or_else(|| 0) 確保提供的是一個閉包。

  1. 沒有初始化所有字段
struct Point {
    x: i32,
    y: i32,
}

let point = Point { x: 5 }; // Error: Missing field `y` in initializer of `Point`

確保初始化結構體的所有字段。

  1. 未考慮運算符優先級
let x = 1 + 2 * 3; // x is 7, not 9

注意操作符的優先級順序。

  1. 忘記在閉包中考慮借用檢查器
let x = 10;
let closure = || println!("{}", x);
drop(x); // Error: `x` moved due to use in closure

閉包引用的外部變量將受到借用檢查器的約束。

  1. 嘗試使用引用來擴展一個 Vec
let mut vec = vec![1, 2, 3];
let items = &[4, 5, 6];
vec.extend(items); // This is correct
vec.extend(&items); // Error: Expected an iterator, found a reference

使用 extend 時確保提供的是一個迭代器。

  1. 混淆數組長度和向量的大小
let array = [1; 10]; // Array of length 10
let vector = vec![1; 10]; // Vector of length 10
println!("{}", array.len()); // Outputs 10
println!("{}", vector.size()); // Error: No method named `size`, should be `len()`

向量 (Vec) 使用 len 方法來獲取長度。

  1. 試圖返回對局部變量的引用
fn get_str() -> &str {
    let s = String::from("hello");
    &s // Error: `s` does not live long enough
}

返回的引用必須擁有有效的生命週期。

  1. 將函數指針與閉包混淆
fn add_one(x: i32) -> i32 {
    x + 1
}
let closure = add_one; // Error: Expected closure, found function pointer
let real_closure = |x| add_one(x);

函數指針和閉包在 Rust 中是不同的類型。

  1. 忽略了變量陰影(變量遮蔽)問題
let x = 5;
let x = x + 1; // Shadows the previous `x`

檢查變量遮蔽(shadowing),確保沒有無意的遮蔽。

  1. 在可以使用迭代器的地方使用了循環
let numbers = vec![1, 2, 3];
let mut doubled = Vec::new();
for num in numbers {
    doubled.push(num * 2);
}

可以使用迭代器的 map 和 collect 的更爲優雅的方式。

  1. 忽略了函數和方法中生命週期的概念
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

當返回引用時,需要確保所有引用都有一個探樣的生命週期。

  1. 在應該使用安全的引用之處使用了原始指針
let x = 5;
let y = &x as *const i32; // Raw pointer, not recommended for regular use

除非確實需要,否則儘量使用安全的借用來代替原始指針。

  1. 錯誤地使用 ? 運算符進行錯誤處理
fn fails() -> Result<(), String> {
    Err("Failed".to_string())
}

fn test() -> Option<(){
    fails()?; // Error: `?` can only be used in functions that return `Result`
    Some(())
}

? 運算符只能用於返回 Result 或 Option 的函數中。

  1. 在進行算術運算時未處理溢出情況
let x: u8 = 255;
let y: u8 = x + 1; // Error: Attempt to add with overflow

使用可檢測溢出的方法,例如 x.checked_add(1)。

  1. 在使用實現了 Copy 特徵的類型時,使用 clone() 代替 &
let x = 5;
let y = x.clone(); // Unnecessary since `i32` implements the `Copy` trait
let z = x; // This is fine

如果類型實現了 Copy trait,應直接賦值代替 clone。

  1. 嘗試修改不可變字符串
let s = "hello";
s.push_str(" world"); // error

解決方案:使用 String 類型而非 & str 類型來創建可變字符串。

let mut s = String::from("hello");
s.push_str(" world");
  1. 錯誤地嘗試使用索引訪問字符串中的字符
let s = "hello";
let c = s[0]; // error

解決方案:使用 chars 方法和迭代器或 char_indices 方法。

let s = "hello";
let c = s.chars().nth(0).unwrap();
  1. 不合理地分割字符串
let s = "hello world";
let first_word: &str = &s[..5]; // 可能截斷了字符邊界

解決方案:確保使用字符邊界來分割。

let s = "hello world";
let space_index = s.find(' ').unwrap_or(s.len());
let first_word: &str = &s[..space_index];
  1. 嘗試直接拼接字符串 slice 和字符串引用
let s1 = "hello";
let s2 = "world";
let s3 = s1 + &s2; // error,`+`運算符後面的字符串必須是`&str`類型

解決方案:確保第一個操作數使用 String,後續的可以是 & str。

let s1 = "hello";
let s2 = "world";
let s3 = s1.to_string() + &s2;
  1. 使用了錯誤的字符拼接方法
let mut s = String::from("hello");
s.push('w''o''r''l''d'); // error

解決方案:正確使用 push 方法來添加單個字符。

let mut s = String::from("hello");
for c in ['w''o''r''l''d'].iter() {
    s.push(*c);
}
  1. 忽略了字符串的 UTF-8 編碼特性而導致錯誤的截取
let s = "你好世界";
let s1 = &s[0..3]; // error: 可能panic因爲不一定是字符邊界

解決方案:使用 chars() 方法和相關的迭代器來正確處理字符。

let s = "你好世界";
let s1: String = s.chars().take(2).collect();
  1. 在字符串字面量中使用轉義序列錯誤
let s = "Hello, \nWorld"; // 不是錯誤,但可能是理解錯誤

解決方案:如果不想讓 \ n 被解析爲換行符,可以使用原始字符串。

let s = r"Hello, \nWorld";
  1. 將 String 和 & str 混淆
fn takes_str(s: &str) {}

let s = String::from("hello");
takes_str(s); // error

解決方案:傳遞字符串引用。

fn takes_str(s: &str) {}

let s = String::from("hello");
takes_str(&s);
本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/-2elCOI-TnZxAwiQJhR9kQ