Rust: Rust 变量与数据类型:从 let 到类型系统全解

最后更新:2026-08-26

在 Rust 中,变量默认不可变——这不是限制,而是一种安全承诺。

Rust 的变量和类型系统是它内存安全承诺的基石。理解不可变性、类型推断和标量类型,是掌握 Rust 的第一步。


1. 你将学到


2. 一个收银员的真实故事

(1) 痛苦:Excel 里改错数字

Lisa 是一家小超市的收银员。每天下班她要用 Excel 统计当天的销售额:

她后来学了 Rust 之后感叹:"如果 Excel 的单元格默认不可修改,我就不用担心覆盖公式了。"

(2) Rust 的变量模型

RUST
fn main() {
    let total_sales: f64 = 1280.50;   // 默认不可变——今天的销售额
    // total_sales = 1500.00;         // ❌ 编译错误:不可以修改不可变变量!

    let mut daily_changes = 0.0;      // 用 mut 声明可变变量
    daily_changes = 150.0;            // ✅ 可以修改
    daily_changes = daily_changes + 200.0;

    const TAX_RATE: f64 = 0.08;      // 编译时常量,全局固定
    println!("税率: {}, 今日应收: {}", TAX_RATE, total_sales * (1.0 + TAX_RATE));
}

Rust 的哲学是:能不变就不要变。默认不可变可以让代码更容易推理,减少意外修改带来的 bug。


3. 变量声明

(1) let:默认不可变

RUST
let x = 5;       // 不可变变量
// x = 10;       // ❌ 编译错误:cannot assign twice to immutable variable

(2) let mut:可变变量

RUST
let mut y = 5;   // 可变变量
y = 10;          // ✅ 可以重新赋值

(3) const:编译时常量

RUST
const MAX_POINTS: u32 = 100_000;   // 必须标注类型
const PI: f64 = 3.1415926535;
特性 let let mut const
可变性 不可变 可变 不可变
运行时存在 否(编译期内联)
类型标注 可省略 可省略 必须
全局作用域
表达式计算 运行时 运行时 编译期常量

4. 标量类型

Rust 有四种基本标量类型:整数、浮点、布尔、字符

100%
graph TB
    A[标量类型] --> B[整数: i8/u8/i16/u16/i32/u32/i64/u64/i128/u128]
    A --> C[浮点: f32/f64]
    A --> D[布尔: bool]
    A --> E[字符: char]

(1) 整数类型

长度 有符号 无符号 范围
8-bit i8 u8 -128 ~ 127 / 0 ~ 255
16-bit i16 u16 -32768 ~ 32767 / 0 ~ 65535
32-bit i32 u32 ±21 亿 / 0 ~ 42 亿
64-bit i64 u64 ±9.2 × 10¹⁸ / 0 ~ 1.8 × 10¹⁹
128-bit i128 u128 极大范围
arch isize usize 与系统位数相同(64 位系统 = i64/u64)

默认整数类型是 i32——性能最佳且够用。

(2) 浮点类型

RUST
let a: f32 = 3.14;    // 32 位浮点(单精度)
let b: f64 = 3.141592653589793;  // 64 位浮点(双精度,默认类型)

默认浮点类型是 f64——现代 CPU 上 f64 和 f32 速度几乎一样,精度更高。

(3) 布尔类型

RUST
let is_ok: bool = true;
let is_not = false;

布尔值常用于条件判断,如 ifwhile 的条件表达式。

(4) 字符类型

RUST
let c: char = 'A';       // ASCII 字符
let emoji: char = '🦀';  // Unicode 字符(4 字节)
let han: char = '锈';    // 中文汉字

Rust 的 charUnicode 标量值,占 4 字节。不是 ASCII!这让 Rust 天然支持国际化。

类型 大小 取值范围 典型用途
bool 1 字节 true / false 条件判断、标志位
char 4 字节 Unicode 标量值 单个字符处理
f32 4 字节 ±3.4×10³⁸(约 7 位精度) 图形计算、GPU 着色器
f64 8 字节 ±1.8×10³⁰⁸(约 15 位精度) 科学计算(默认浮点)
i32 4 字节 ±21 亿 通用整数(默认)
u8 1 字节 0 ~ 255 字节数据、RGB 值
usize arch 与系统位数相同 数组索引、容器大小

5. 类型推断与标注

▶ 示例 1:类型推断(难度 ⭐)

RUST
// ============================================
// Rust 编译器根据赋值推断类型
// ============================================

fn main() {
    let x = 42;           // 推断为 i32(默认整数类型)
    let y = 3.14;         // 推断为 f64(默认浮点类型)
    let z = true;         // 推断为 bool
    let c = 'R';          // 推断为 char

    // 可以用 :type 查看(本节示例,不是标准方法)
    println!("x 的值是: {}", x);
    println!("y 的值是: {}", y);
}

输出:

TEXT 📖 仅展示
x 的值是: 42
y 的值是: 3.14

编译器会根据上下文自动推断类型,开发者不需要在每一处都写类型标注。


▶ 示例 2:显式类型标注(难度 ⭐)

RUST
// ============================================
// 显式指定类型——当需要特定大小或精度时
// ============================================

fn main() {
    let small: u8 = 255;           // u8 最大值
    let big: i64 = 9_223_372_036_854_775_807;  // 下划线可作数字分隔符
    let precise: f32 = 3.141592653589793;        // f32 会截断精度
    let flag: bool = 5 > 3;                      // 布尔表达式的结果

    println!("small: {}, big: {}, precise: {:.10}, flag: {}", small, big, precise, flag);
}

输出:

TEXT 📖 仅展示
small: 255, big: 9223372036854775807, precise: 3.1415927410, flag: true

注意 f32 的精度只有约 7 位有效数字,3.141592653589793 被截断为 3.1415927410


▶ 示例 3:变量遮蔽(Shadowing)(难度 ⭐⭐)

RUST
// ============================================
// Shadowing:用同一个名称覆盖之前的变量
// ============================================

fn main() {
    let x = 5;              // 第一个 x
    let x = x + 1;          // 遮蔽:第二个 x,值变成 6
    let x = x * 2;          // 遮蔽:第三个 x,值变成 12

    // 遮蔽允许改变类型!
    let name = "Rust";       // &str 类型
    let name = name.len();   // 变成了 usize 类型!

    println!("x 的值: {}", x);
    println!("name 的长度: {}", name);
}

输出:

TEXT 📖 仅展示
x 的值: 12
name 的长度: 4

遮蔽 ≠ 可变。 遮蔽创建了一个新变量(新内存),而 mut 是修改同一块内存。遮蔽可以在同一作用域改变变量类型,mut 不行。


▶ 示例 4:综合练习——库存管理小工具(难度 ⭐⭐⭐)

RUST
// ============================================
// 综合示例:用变量、遮蔽和常量模拟库存管理
// 演示 let/mut/const/shadowing 的实际应用
// ============================================

const MAX_STOCK: u32 = 10_000;
const DISCOUNT_THRESHOLD: u32 = 500;

fn main() {
    let item_name = "Wireless Mouse";
    let price: f64 = 49.99;
    let mut stock: u32 = 2500;

    println!("=== 库存管理系统 ===");
    println!("商品: {}", item_name);
    println!("单价: {:.2} 元", price);
    println!("库存: {} 件", stock);

    stock = stock - 300;
    println!("\n--- 今日出货 300 件 ---");
    println!("剩余库存: {} 件", stock);

    let price = price * 0.85;
    println!("\n--- 会员折扣 85 折 ---");
    println!("会员价: {:.2} 元", price);

    let price = format!("{:.2} 元", price);
    println!("价格标签: {}", price);

    let total_value = (stock as f64) * 49.99;
    println!("\n库存总价值: {:.2} 元", total_value);

    if stock > DISCOUNT_THRESHOLD {
        println!("库存充足(> {}),无需补货", DISCOUNT_THRESHOLD);
    } else {
        let needed = MAX_STOCK - stock;
        println!("库存不足!建议补货 {} 件", needed);
    }

    println!("最大容量: {} 件", MAX_STOCK);
}

输出:

TEXT 📖 仅展示
=== 库存管理系统 ===
商品: Wireless Mouse
单价: 49.99 元
库存: 2500 件

--- 今日出货 300 件 ---
剩余库存: 2200 件

--- 会员折扣 85 折 ---
会员价: 42.49 元
价格标签: 42.49 元

库存总价值: 109978.00 元
库存充足(> 500),无需补货
最大容量: 10000 件

此示例综合运用了 const 常量、let 不可变变量、let mut 可变变量和变量遮蔽(遮蔽改变类型:f64String),展示了实际场景中各变量声明方式的配合使用。


▶ 示例 5:整数溢出与安全运算(难度 ⭐⭐)

RUST
// ============================================
// 整数溢出行为:debug 模式 panic vs release 模式回绕
// ============================================

fn main() {
    let max_u8: u8 = 255;
    println!("u8 最大值: {}", max_u8);

    // 安全运算方法(不会 panic,返回溢出结果)
    let (result, did_overflow) = max_u8.overflowing_add(1);
    println!("255 + 1 (overflowing_add): {},是否溢出: {}", result, did_overflow);

    let checked = max_u8.checked_add(1);
    println!("255 + 1 (checked_add): {:?}", checked);

    let saturating = max_u8.saturating_add(100);
    println!("255 + 100 (saturating_add): {}", saturating);

    let wrapping: u8 = max_u8.wrapping_add(1);
    println!("255 + 1 (wrapping_add): {}", wrapping);

    let neg_i8: i8 = -128;
    let (abs_result, overflow) = neg_i8.overflowing_abs();
    println!("i8 最小值取绝对值: {},是否溢出: {}", abs_result, overflow);
}

输出:

TEXT 📖 仅展示
u8 最大值: 255
255 + 1 (overflowing_add): 0,是否溢出: true
255 + 1 (checked_add): None
255 + 100 (saturating_add): 255
255 + 1 (wrapping_add): 0
i8 最小值取绝对值: -128,是否溢出: true

Rust 在 debug 模式下整数溢出会 panic,release 模式会回绕(wrap around)。推荐使用 checked_*saturating_*wrapping_* 系列方法来显式控制溢出行为。


▶ 示例 6:变量遮蔽与类型转换实战(难度 ⭐⭐⭐)

RUST
// ============================================
// 变量遮蔽实战:字符串解析链
// ============================================

fn main() {
    let input = "42.5";
    println!("原始输入: {} (类型: &str)", input);

    let input = input.parse::<f64>();
    println!("解析结果: {:?}", input);

    let input = match input {
        Ok(value) => value,
        Err(_) => 0.0,
    };
    println!("解包后: {} (类型: f64)", input);

    let input = input as i32;
    println!("转整数: {} (类型: i32,小数截断)", input);

    let input = input * 2;
    println!("翻倍后: {} (类型: i32)", input);

    let input = format!("结果: {}", input);
    println!("最终输出: {} (类型: String)", input);

    let alice_score = "95";
    let alice_score = alice_score.parse::<u32>().unwrap_or(0);
    let alice_grade = if alice_score >= 90 { "A" } else { "B" };
    println!("Alice: 分数={}, 等级={}", alice_score, alice_grade);
}

输出:

TEXT 📖 仅展示
原始输入: 42.5 (类型: &str)
解析结果: Ok(42.5)
解包后: 42.5 (类型: f64)
转整数: 42 (类型: i32,小数截断)
翻倍后: 84 (类型: i32)
最终输出: 结果: 84 (类型: String)
Alice: 分数=95, 等级=A

变量遮蔽允许在同一个作用域中逐步转换数据类型——从 &strResult<f64>f64i32String,每一步都用同一个变量名,代码清晰无冗余。


❓ 常见问题

Q i32 和 u32 有什么区别?
A i 开头是有符号(可正可负),u 开头是无符号(只能非负)。
Q 为什么 Rust 默认 i32 而不是 i64?
A 性能权衡。
Q 遮蔽(shadowing)和 let mut 有什么区别?
A 遮蔽创建新变量(可换类型),mut 修改同个变量(类型不变)。
Q 字符 char 占 4 字节,那字符串不是浪费空间吗?
A Rust 的字符串不是 char 数组。
Q const 和 let 有什么区别?
A const 编译期内联,let 运行时分配。

📖 小节


📝 作业

  1. 难度 ⭐:声明一个 u16 类型的变量并赋值为 65535,然后尝试改成 65536,观察编译器的报错信息。
  2. 难度 ⭐⭐:写一个程序,用 let mut 声明可变变量累计 1 到 5 的和,输出结果。
  3. 难度 ⭐⭐⭐:尝试在 fn main() 外部声明一个 const 常量并在函数内使用。然后尝试在函数外声明一个 let 变量,观察编译器报错——体会 const 的全局性。
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏