Rustlings Arena
← 返回竞技场

常见 Rust 编译器错误

最常见的 Rust 错误,附示例和修复方案。

E0382

使用了已移动的值

error[E0382]: use of moved value: `s1`

当你把堆分配的值(如 String 或 Vec)赋给另一个变量时,所有权转移到新变量,原变量不再有效——你无法继续使用它。这是 Rust 所有权系统在编译期消除"释放后使用"Bug 的机制。

❌ Broken
let s1 = String::from("hello");
let s2 = s1;          // s1 moves into s2
println!("{}", s1);   // ❌ E0382: s1 was moved
✅ Fixed
let s1 = String::from("hello");
let s2 = s1.clone();  // deep copy — both stay valid
println!("{} {}", s1, s2); // ✅

// Or borrow instead of moving:
let s2 = &s1;
println!("{} {}", s1, s2); // ✅
💡 Tip: 基本类型(i32、bool、char、f64)实现了 Copy,会自动复制。堆类型(String、Vec、Box)则会移动。
E0502

不能将值借用为可变的——它同时已被借用为不可变的

error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutable

Rust 允许多个共享引用(&)或一个独占可变引用(&mut)——两者不能同时存在。如果你持有活跃的不可变借用,在它结束之前不能再取得可变借用。这条规则从根本上消除了数据竞争。

❌ Broken
let mut v = vec![1, 2, 3];
let first = &v[0];    // immutable borrow
v.push(4);            // ❌ E0502: mutable borrow while immutable exists
println!("{}", first);
✅ Fixed
let mut v = vec![1, 2, 3];
let first = v[0];     // copy the value out (i32 is Copy)
v.push(4);            // ✅ no active borrow
println!("{}", first);

// Or restructure scope:
let mut v = vec![1, 2, 3];
{
    let first = &v[0];
    println!("{}", first);
}   // first's borrow ends here
v.push(4);            // ✅
💡 Tip: 借用检查器比较保守。如果它一直报错,可以尝试把 println 移到前面、复制值而非借用,或使用 .clone()。
E0499

不能多次将值借用为可变的

error[E0499]: cannot borrow `v` as mutable more than once at a time

同一时刻只能存在一个可变引用(&mut)。这防止了单线程代码中的数据竞争,也是 Rust 并发保证的基础规则。

❌ Broken
let mut v = vec![1, 2, 3];
let a = &mut v;
let b = &mut v;   // ❌ E0499: second mutable borrow
a.push(4);
b.push(5);
✅ Fixed
let mut v = vec![1, 2, 3];
{
    let a = &mut v;
    a.push(4);
}   // a's borrow ends here
let b = &mut v;   // ✅ now safe
b.push(5);
💡 Tip: 使用作用域块 {} 来限制可变借用的存活时间。实践中这个错误通常意味着需要重构逻辑,使可变操作顺序发生。
E0308

类型不匹配

error[E0308]: mismatched types expected `i32`, found `&i32`

编译器推断或期望的类型与你提供的不符。在迭代时(迭代器产生引用)、函数返回 () 而非实际值时,或混用有符号/无符号整数时非常常见。

❌ Broken
// Trailing semicolon returns () instead of i32:
fn double(n: i32) -> i32 {
    n * 2;   // ❌ E0308: returns () not i32
}

// Iterator reference confusion:
let nums = vec![1, 2, 3];
let sum: i32 = nums.iter().sum(); // needs type annotation
✅ Fixed
fn double(n: i32) -> i32 {
    n * 2    // ✅ no semicolon — expression returned
}

// Dereference in closures when iterating:
let nums = vec![1, 2, 3];
let doubled: Vec<i32> = nums.iter().map(|&x| x * 2).collect();
//                                       ^ dereference the &i32
💡 Tip: 函数最后一行末尾加分号会悄然将返回类型改为 ()。这是 Rust 中初学者最常犯的错误 #1。
E0277

特征约束不满足

error[E0277]: `MyType` doesn't implement `std::fmt::Display`

该类型没有实现所需的特征。最常见的触发场景:用 {} 格式化却没有实现 Display、比较类型却没有实现 PartialOrd、调用需要特定特征的方法。

❌ Broken
struct Point { x: i32, y: i32 }

let p = Point { x: 1, y: 2 };
println!("{}", p);  // ❌ E0277: Point doesn't implement Display
println!("{:?}", p); // also fails — needs Debug
✅ Fixed
#[derive(Debug)]
struct Point { x: i32, y: i32 }

let p = Point { x: 1, y: 2 };
println!("{:?}", p);  // ✅ Debug via #[derive(Debug)]

// Implement Display manually for custom formatting:
use std::fmt;
impl fmt::Display for Point {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "({}, {})", self.x, self.y)
    }
}
println!("{}", p);  // ✅
💡 Tip: 为 {:?} 打印添加 #[derive(Debug)]。对于 {} 打印,需要手动实现 std::fmt::Display。大多数缺失的约束是 Debug、Display、Clone、PartialEq 或 PartialOrd。
E0106

缺少生命周期说明符

error[E0106]: missing lifetime specifier expected named lifetime parameter

当函数返回一个引用且有多个引用参数时,Rust 无法推断输出引用来自哪个输入。你需要用生命周期标注来告知编译器。

❌ Broken
// Compiler doesn't know if the return borrows from x or y:
fn longest(x: &str, y: &str) -> &str {   // ❌ E0106
    if x.len() > y.len() { x } else { y }
}
✅ Fixed
// 'a says: output lives no longer than the shorter input
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {  // ✅
    if x.len() > y.len() { x } else { y }
}

// Single input → no annotation needed (elision rule):
fn first_word(s: &str) -> &str {  // ✅ lifetime elided
    let bytes = s.as_bytes();
    for (i, &b) in bytes.iter().enumerate() {
        if b == b' ' { return &s[..i]; }
    }
    &s[..]
}
💡 Tip: 生命周期标注是约束,不是持续时间。'a 的含义是"输出的存活时间不超过输入"。大多数生命周期会被自动推断(省略)。只有当编译器无法确定关系时,才需要显式标注。
E0596

不能将值借用为可变的——未声明为可变

error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable

你试图修改一个未用 mut 声明的变量。在 Rust 中,变量默认不可变——编译器强制执行这一点,以便在编译期捕获意外的修改。

❌ Broken
let x = 5;
x = 10;        // ❌ E0596: x is immutable

let v = vec![1, 2, 3];
v.push(4);     // ❌ E0596: v is immutable
✅ Fixed
let mut x = 5;   // ✅ mut makes it mutable
x = 10;

let mut v = vec![1, 2, 3];
v.push(4);     // ✅
💡 Tip: 在 let 后面加上 mut。只有当变量确实需要改变时才加 mut——默认不可变能捕获 Bug,也让代码更易推理。
E0384

不能对不可变变量进行二次赋值

error[E0384]: cannot assign twice to immutable variable `x`

与 E0596 类似,但触发于第二次赋值而非方法调用。变量声明时没有 mut,却试图重新赋值。

❌ Broken
let x = 5;
x = 10;  // ❌ E0384
✅ Fixed
let mut x = 5;
x = 10;  // ✅

// Shadowing is another option if you want to "replace" the variable:
let x = 5;
let x = 10;  // ✅ shadowing — creates a new binding named x
💡 Tip: 遮蔽(let x = ...)和可变(mut x = ...)看起来相似,但不同:遮蔽创建新变量(可以改变类型),可变性修改同一绑定。
E0507

不能移出借用的内容

error[E0507]: cannot move out of `*s` which is behind a shared reference

你试图从引用中移出一个值。你无法通过 &T 获取所有权——引用并不拥有数据。

❌ Broken
fn print_name(s: &String) {
    let owned = *s;  // ❌ E0507: can't move out of &String
    println!("{}", owned);
}
✅ Fixed
fn print_name(s: &String) {
    let owned = s.clone();  // ✅ clone the data
    println!("{}", owned);
}

// Or just use the reference directly:
fn print_name(s: &String) {
    println!("{}", s);  // ✅ no need to own it
}
💡 Tip: 如果只需要读取值,直接使用引用即可。如果需要拥有的副本,调用 .clone()。如果需要所有权,将参数改为接受 T 而非 &T。
E0004

match 中存在非穷举的模式

error[E0004]: non-exhaustive patterns: `None` not covered

match 表达式必须处理每一种可能的情况。如果有任何变体未被处理,代码无法编译。这消除了整类因未处理情况导致崩溃的运行时 Bug。

❌ Broken
let opt: Option<i32> = Some(42);
match opt {
    Some(n) => println!("{}", n),
    // ❌ E0004: None not covered
}
✅ Fixed
match opt {
    Some(n) => println!("{}", n),
    None    => println!("nothing"),  // ✅ exhaustive
}

// Use _ as a catch-all:
match opt {
    Some(n) => println!("{}", n),
    _       => {},  // ✅ handles everything else
}

// Or use if let for a single case:
if let Some(n) = opt {
    println!("{}", n);  // ✅ no else needed
}
💡 Tip: 编译器会精确告知哪些变体缺失。_ 是通配符。if let 是只关心一种模式时的简写。
E0072

递归类型具有无限大小

error[E0072]: recursive type `List` has infinite size

如果递归类型的每个变体直接包含自身,分配它将需要无限内存。Rust 需要在编译期知道每种类型的大小。解决方法是通过 Box 进行间接引用,Box 的大小是已知的指针大小。

❌ Broken
enum List {
    Cons(i32, List),  // ❌ E0072: infinite size
    Nil,
}
✅ Fixed
enum List {
    Cons(i32, Box<List>),  // ✅ Box has fixed pointer size (8 bytes)
    Nil,
}

let list = List::Cons(1, Box::new(List::Cons(2, Box::new(List::Nil))));
💡 Tip: Box<T> 是最简单的堆指针。无论 T 是什么,它都有固定大小(一个指针宽度),从而打破递归的大小计算。
E0716

临时值在被借用期间被释放

error[E0716]: temporary value dropped while borrowed

你创建了一个临时值,获取了它的引用,但临时值在引用被使用之前就被释放了。引用将成为悬垂引用——指向已释放的内存。

❌ Broken
let s: &str = &String::from("hello")
    .to_uppercase();  // ❌ E0716: temporary dropped

// Also common with chains:
let name = get_user().name;  // if get_user() returns a temp
✅ Fixed
// Bind the temporary to a variable first:
let upper = String::from("hello").to_uppercase();
let s: &str = &upper;  // ✅ upper lives long enough
println!("{}", s);
💡 Tip: 遇到此错误时,引入一个变量来持有中间值。let 绑定的生命周期延伸到所在块的结尾。
E0515

不能返回引用局部变量的值

error[E0515]: cannot return value referencing local variable `local`

你试图返回指向局部变量的引用。函数返回时,局部变量被释放,引用将悬垂。Rust 在编译期阻止了这一点。

❌ Broken
fn get_greeting() -> &str {
    let s = String::from("hello");
    &s   // ❌ E0515: s is dropped when function returns
}
✅ Fixed
// Return an owned value:
fn get_greeting() -> String {
    String::from("hello")  // ✅ caller owns it
}

// Or return a &'static str literal:
fn get_greeting() -> &'static str {
    "hello"  // ✅ lives for the entire program
}
💡 Tip: 如果需要返回引用,它必须来自函数的输入(从参数借用)或具有 'static 生命周期。有疑问时,返回拥有所有权的类型(String、Vec 等)。
E0369

二元运算无法应用于该类型

error[E0369]: binary operation `>` cannot be applied to type `T`

你对泛型类型 T 使用了运算符,但 T 没有约束要求该运算符存在。泛型代码必须明确声明类型支持哪些操作。

❌ Broken
fn largest<T>(list: &[T]) -> &T {
    let mut biggest = &list[0];
    for item in list {
        if item > biggest {  // ❌ E0369: T doesn't implement PartialOrd
            biggest = item;
        }
    }
    biggest
}
✅ Fixed
fn largest<T: PartialOrd>(list: &[T]) -> &T {  // ✅ add bound
    let mut biggest = &list[0];
    for item in list {
        if item > biggest {
            biggest = item;
        }
    }
    biggest
}
💡 Tip: 遇到 E0369 时,错误信息会告诉你需要哪个特征:比较运算需要 PartialOrd,+ 运算需要 Add,{} 格式化需要 Display 等。将其添加为约束:<T: 特征名>。
E0428

名称被多次定义

error[E0428]: the name `connect` is defined multiple times

你在同一作用域内定义了两个同名的条目(函数、结构体、类型等)。Rust 不允许这样做——名称在模块内必须唯一。

❌ Broken
fn connect() -> String { "tcp".into() }
fn connect() -> String { "udp".into() }  // ❌ E0428: duplicate
✅ Fixed
fn connect_tcp() -> String { "tcp".into() }
fn connect_udp() -> String { "udp".into() }  // ✅ unique names

// Or use a parameter:
fn connect(protocol: &str) -> String { protocol.into() }
💡 Tip: use 语句导入冲突的名称时也会触发此错误。使用 as 重命名:use std::io::Error as IoError;
E0061

参数数量错误

error[E0061]: this function takes 2 arguments but 3 arguments were supplied

你调用函数时传入的参数多于或少于其签名声明的数量。Rust 不支持默认参数或可变参数函数(宏如 println! 除外)。

❌ Broken
fn add(a: i32, b: i32) -> i32 { a + b }

add(1, 2, 3);  // ❌ E0061: 3 args, expected 2
add(1);        // ❌ E0061: 1 arg, expected 2
✅ Fixed
add(1, 2);  // ✅

// For optional parameters, use Option:
fn greet(name: &str, title: Option<&str>) {
    match title {
        Some(t) => println!("{} {}", t, name),
        None    => println!("{}", name),
    }
}
greet("Alice", None);
greet("Bob", Some("Dr."));
💡 Tip: Rust 没有默认参数。对可选值使用 Option<T>,对有许多可选字段的函数使用构建器结构体。
E0433

解析失败——在作用域中找不到该名称

error[E0433]: failed to resolve: use of undeclared crate or module `HashMap`

你使用了不在当前作用域中的类型或函数。对于预导入之外的标准库类型(如 HashMap、BTreeMap、BufReader),你需要显式的 use 语句。

❌ Broken
let mut map = HashMap::new();  // ❌ E0433: HashMap not in scope
✅ Fixed
use std::collections::HashMap;

let mut map = HashMap::new();  // ✅

// Common imports:
use std::collections::{HashMap, HashSet, BTreeMap};
use std::io::{self, BufRead, Write};
use std::fmt;
💡 Tip: Rust 预导入自动引入常用类型(Vec、String、Option、Result 等)。其他所有类型都需要显式 use。Rust Analyzer 会建议正确的 use 路径。

期望分号 / 期望表达式

error: expected `;`, found `let` error: expected expression, found keyword `fn`

语法错误——解析器遇到了意外内容。通常是缺少分号、未关闭的定界符、关键字拼写错误或错位的表达式。

❌ Broken
fn main() {
    let x = 5     // ❌ missing semicolon
    let y = 10;

    fn inner() {} // ❌ nested fn needs to be at statement level
    x + y         // if this is a statement, not a return, add ;
}
✅ Fixed
fn main() {
    let x = 5;     // ✅ semicolon added
    let y = 10;

    fn helper() {}  // ✅ nested fn is valid in Rust
    println!("{}", x + y);
}
💡 Tip: Rust 的错误信息几乎总是指向正确的行。如果分号错误看起来位置不对,检查上一行——上一条语句缺少 ; 会让解析器进入奇怪的状态。

求值特征约束时溢出

error[E0275]: overflow evaluating the requirement `Box<T>: Sized`

编译器在证明特征约束时陷入了无限循环。最常见的原因是递归的特征实现,或某个类型试图实现一个需要自身的特征。

❌ Broken
// Generic function calling itself with an incompatible bound:
fn process<T: Clone>(x: T) {
    process(x.clone()); // infinite recursion in type inference
}
✅ Fixed
// Add a base case or restructure logic:
fn process(x: String) {
    if x.is_empty() { return; }
    process(x[1..].to_string());
}
💡 Tip: 这个错误几乎总是指向递归类型或特征约束循环。简化特征约束或添加显式类型参数。

在当前作用域中找不到该值

error[E0425]: cannot find value `x` in this scope

你引用了一个在当前作用域中不存在的变量。常见原因:变量名拼写错误、变量在已结束的内部块中声明、在循环外使用了循环变量。

❌ Broken
{
    let x = 5;
}
println!("{}", x);  // ❌ x is out of scope

for i in 0..10 { }
println!("{}", i);  // ❌ i only lives inside the for loop
✅ Fixed
let x = 5;         // declare outside the block
{
    println!("{}", x); // ✅ x is in scope
}
println!("{}", x); // ✅ still in scope

// Save the last loop value explicitly:
let mut last = 0;
for i in 0..10 { last = i; }
println!("{}", last);  // ✅
💡 Tip: Rust 的作用域以块为基础。变量只存在于声明处到其所在块结尾之间。如果需要更宽的作用域,将声明移到外层。
🦀

准备好了吗?

26 个免费互动挑战。无需安装,无需注册,马上开始。

开始学习 Rust →