Skip to content

调试与错误

> 认识常见编译错误信息的结构,掌握利用编译器提示快速定位和修复问题的方法。

常见错误

错误 1:修改变量忘记 mut

概念名称: 尝试修改不可变变量会触发编译错误 E0384。

错误信息:
┌──────────────────────────────────────┐
│  error[E0384]: cannot assign twice   │
│  to immutable variable `x`           │
│                                       │
│  E0384 = "不能给不可变变量赋值两次"   │
│  解决方案:添加 mut 使变量可变         │
└──────────────────────────────────────┘

最简示例

rust
fn main() {
    let x = 10;
    // x = 20;  // ❌ E0384: 不能修改不可变变量

    let mut y = 10;  // ✅ 正确
    y = 20;
    println!("y = {}", y);
}
▶ Run

详细示例

rust
fn main() {
    // 错误场景:忘记 mut
    let count = 0;
    // count += 1;  // ❌ 编译错误:不能修改不可变变量

    // 修复方案 1:添加 mut
    let mut count = 0;
    count += 1;
    println!("count = {}", count);

    // 修复方案 2:使用遮蔽
    let count = 0;
    let count = count + 1;  // 新变量遮蔽旧变量
    println!("count = {}", count);
}
▶ Run

关键代码说明:

代码含义为什么这样写
let x = 10不可变变量Rust 默认不可变,安全优先
let mut y = 10可变变量显式声明可变,明确意图
let count = count + 1遮蔽不修改原值,创建新值

错误 2:变量未初始化就使用

概念名称: 使用未初始化的变量会触发编译错误 E0381。

错误信息:
┌──────────────────────────────────────┐
│  error[E0381]: used binding `x` is   │
│  not initialized                     │
│                                       │
│  E0381 = "使用了未初始化的变量"       │
│  解决方案:使用前必须赋值              │
└──────────────────────────────────────┘

最简示例

rust
fn main() {
    let x: i32;
    // println!("{}", x);  // ❌ E0381: 未初始化
}
▶ Run

详细示例

rust
fn main() {
    // 错误场景:声明但未初始化
    let x: i32;
    // println!("{}", x);  // ❌ 编译错误

    // 修复方案 1:立即初始化
    let x: i32 = 0;
    println!("x = {}", x);

    // 修复方案 2:条件初始化
    let x: i32;
    let condition = true;
    if condition {
        x = 10;
    } else {
        x = 0;
    }
    println!("x = {}", x);  // ✅ 所有分支都初始化了
}
▶ Run

关键代码说明:

代码含义为什么这样写
let x: i32声明未初始化必须标注类型,编译器无法推断
x = 10延迟初始化所有代码路径必须初始化

错误 3:类型推断失败

概念名称: 编译器无法推断变量类型时需要显式标注。

错误信息:
┌──────────────────────────────────────┐
│  error[E0282]: type annotations needed│
│                                       │
│  E0282 = "需要类型标注"               │
│  解决方案:显式标注类型                │
└──────────────────────────────────────┘

最简示例

rust
fn main() {
    let x: i32;  // ✅ 显式标注类型
    x = 10;
    println!("x = {}", x);
}
▶ Run

详细示例

rust
fn main() {
    // 错误场景:无法推断类型
    // let x;  // ❌ 编译器不知道 x 是什么类型
    // x = 10;

    // 修复方案 1:显式标注
    let x: i32;
    x = 10;

    // 修复方案 2:声明时初始化
    let x = 10;  // ✅ 推断为 i32

    // 修复方案 3:使用默认值
    let x = i32::default();  // 0
    println!("x = {}", x);
}
▶ Run

关键代码说明:

代码含义为什么这样写
let x: i32显式类型标注编译器无法推断时需要
let x = 10类型推断i32 是默认整数类型
i32::default()默认值0,适合初始化

调试技巧

使用 dbg! 宏

概念名称: dbg! 宏打印表达式及其结果,包含文件名和行号。

语法结构:
┌──────────────────────────────────────┐
│  dbg!(表达式);                        │
│                                       │
│  输出:[文件名:行号] 表达式 = 结果    │
│  返回值:表达式的值                   │
└──────────────────────────────────────┘

为什么用它?

rust
// 没有:手动打印,缺少上下文
let sum = x + y;
println!("sum = {}", sum);  // 不知道在哪打印的

// 有:自动包含文件名和行号
let sum = dbg!(x + y);
// 输出:[src/main.rs:5] x + y = 30
▶ Run

最简示例

rust
fn main() {
    let x = 10;
    let y = 20;
    let sum = dbg!(x + y);
    println!("sum = {}", sum);
}
▶ Run

详细示例

rust
fn main() {
    let x = 10;
    let y = 20;

    // dbg! 打印表达式和结果
    let sum = dbg!(x + y);

    // 输出:
    // [src/main.rs:5] x + y = 30

    // 可以链式使用
    let result = dbg!(dbg!(x * 2) + dbg!(y * 3));
    println!("result = {}", result);
}
▶ Run

关键代码说明:

代码含义为什么这样写
dbg!(x + y)调试打印自动打印文件名、行号、结果
dbg!(dbg!(x))链式调试可嵌套,追踪中间值
返回值返回表达式值不影响原代码逻辑

使用 cargo expand

安装:

bash
cargo install cargo-expand

查看宏展开:

bash
cargo expand

小结

本章我们学习了:

  • println! 宏的详细用法
  • ✅ 变量声明和可变性
  • ✅ 类型推断和显式标注
  • ✅ 常量和静态变量
  • ✅ 变量遮蔽
  • ✅ 注释的写法

关键概念

概念说明
let声明变量
let mut声明可变变量
const常量(必须标注类型)
println!打印宏(带换行)
print!打印宏(不带换行)
遮蔽重新使用变量名
dbg!调试宏(打印表达式和结果)

练习题

详见:练习题