Rust 学习(2)-变量、常量与 shadowing

文章目录

返回首页 | 上篇 | 下篇


理解 Rust 的变量绑定机制 ------ 为什么默认不可变?什么是 shadowing?


一、回顾与导入

上一篇文章我们完成了 Rust 的环境搭建,并跑通了第一个 Hello World 程序。

在正式写代码之前,我们需要先理解 Rust 中最基础的概念:变量。Rust 的变量设计和你之前接触的语言(C/Java/Python/JS)有很大不同,让我们来看看。


二、变量:默认是不可变的

在 Rust 中,变量默认是不可变的(immutable)。这意味着一旦给变量绑定了值,就不能再修改它。

rust 复制代码
fn main() {
    let x = 5;
    println!("x = {}", x);

    x = 6;  // ❌ 编译错误!
    println!("x = {}", x);
}

编译时会报错:

log 复制代码
error[E0384]: cannot assign twice to immutable variable `x`

💡 这其实是 Rust 的设计理念之一:默认安全,需要可变才显式指定。这样可以避免很多意外修改引起的 bug。

如何让变量可变?

使用 mut 关键字(mutable 的缩写):

rust 复制代码
fn main() {
    let mut x = 5;
    println!("x = {}", x);

    x = 6;  // ✅ 可以修改了
    println!("x = {}", x);
}

输出:

log 复制代码
x = 5
x = 6

三、变量 vs 常量

Rust 中还有 const(常量),它和 let 变量有几个关键区别:

特性 let 变量 const 常量
是否可变为 mut ❌ 永远不可变
作用域 块作用域 全局或模块作用域
值必须编译时已知 ❌(运行时也行) ✅ 必须是常量表达式
类型标注 可省略(类型推断) ✅ 必须显式标注
rust 复制代码
const MAX_POINTS: u32 = 100_000;  // 常量名约定全大写,下划线分隔
const PI: f64 = 3.14159265359;

fn main() {
    println!("Max points: {}", MAX_POINTS);
    println!("PI: {}", PI);

    // MAX_POINTS = 200_000;  // ❌ 不能修改常量
}

什么时候用 const

  • 数学常量(PI、E)
  • 配置值(最大连接数、超时时间)
  • 任何在程序运行期间不会改变的值

四、Shadowing(变量遮蔽)

这是 Rust 中一个非常有趣且实用的特性:可以用同名变量来"遮蔽"之前的变量

rust 复制代码
fn main() {
    let x = 5;
    let x = x + 1;  // 新的 x 遮蔽了旧的 x
    {
        let x = x * 2;  // 在内部作用域中再次遮蔽
        println!("内部作用域: x = {}", x);  // 输出: 12
    }
    println!("外部作用域: x = {}", x);  // 输出: 6
}

Shadowing vs mut:有什么区别?

很多人会混淆这两个概念,我们来对比一下:

操作 let x = ...(shadowing) let mut x = ...
改变值类型 ✅ 可以改变类型 ❌ 不能改变类型
改变值 ✅ 重新绑定 ✅ 通过赋值
内存地址 改变(新绑定了内存) 不变

重点来了:shadowing 可以改变变量的类型

rust 复制代码
fn main() {
    let x = "hello";      // 字符串类型 &str
    let x = x.len();      // 变成了 usize 类型 ✅
    println!("x = {}", x); // 输出: 5

    // 如果用 mut,则不能改类型
    let mut y = "world";
    // y = y.len();       // ❌ 编译错误:不能把 usize 赋值给 &str
}

这个特性在需要"转换"一个值时非常有用,比如字符串转数字,并保持变量名不变。

实际应用场景

rust 复制代码
fn main() {
    // 场景1:数据转换过程中复用变量名
    let data = "  42  ";
    let data = data.trim();      // &str -> &str
    let data = data.parse::<i32>().unwrap();  // &str -> i32
    println!("计算结果: {}", data + 10);  // 52

    // 场景2:处理用户输入
    let input = "   hello   ";
    let input = input.trim();  // 去除空格后重新绑定
    println!("处理后的输入: '{}'", input);  // 'hello'
}

五、完整代码示例

把今天学的内容整合起来:

rust 复制代码
// 常量声明在模块作用域
const APP_NAME: &str = "Rust Learning";
const VERSION: u32 = 1;

fn main() {
    println!("=== 常量示例 ===");
    println!("App: {}, v{}", APP_NAME, VERSION);

    println!("\n=== 可变变量示例 ===");
    let mut counter = 0;
    while counter < 3 {
        println!("counter = {}", counter);
        counter += 1;
    }

    println!("\n=== Shadowing 示例 ===");
    let value = "  123  ";
    println!("原始: '{}'", value);

    let value = value.trim();
    println!("trim后: '{}'", value);

    let value = value.parse::<i32>().unwrap();
    println!("转换数字后: {}", value);

    let value = value * 2;
    println!("乘以2后: {}", value);
}

运行结果:

log 复制代码
=== 常量示例 ===
App: Rust Learning, v1

=== 可变变量示例 ===
counter = 0
counter = 1
counter = 2

=== Shadowing 示例 ===
原始: '  123  '
trim后: '123'
转换数字后: 123
乘以2后: 246

六、总结与速查卡

概念 语法 特点
不可变变量 let x = 5 默认,不能修改值
可变变量 let mut x = 5 可以修改值,但不能改变类型
常量 const MAX: u32 = 100 永远不可变,编译时确定,要显式类型
Shadowing let x = ...; let x = ... 重新绑定,可改变类型,旧变量被遮蔽

最佳实践建议

  1. 优先使用不可变变量(let),必要时再加 mut
  2. 常量大写加下划线,放在文件顶部
  3. 需要转换类型时优先考虑 shadowing,而不是创建新变量名(比如 x_strx_int 这种)

七、思考题

  1. 下面的代码会输出什么?为什么?
rust 复制代码
fn main() {
    let x = 10;
    let x = "hello";
    println!("{}", x);
}
  1. mut 和 shadowing 哪个性能更好?为什么?

参考链接

返回首页 | 上篇 | 下篇

相关推荐
AndrewHZ25 分钟前
【LLM技术全景】阶段总结:技术原理篇核心知识回顾
人工智能·深度学习·算法·语言模型·大模型·llm·芯片开发
小星星闪亮登场1 小时前
2026萌新联赛第三场-- (郑州轻工业大学)
数据结构·c++·经验分享·算法·贪心算法·排序算法·深度优先
云贝教育-郑老师2 小时前
OceanBase 是如何搞定海量数据分片(Sharding)的?
学习·oceanbase·dba
冻柠檬飞冰走茶2 小时前
PTA基础编程题目集 7-8超速判断(C++语言实现)
开发语言·数据结构·c++·算法
编程圈子2 小时前
电机驱动开发学习23. 保护体系与故障状态机
驱动开发·学习
玖玥拾2 小时前
LeetCode 88 合并两个有序数组
算法·leetcode
数据皮皮侠AI2 小时前
上市公司数字供应链金融指数(2010-2024)
大数据·人工智能·算法
小程故事多_803 小时前
用GRPO算法重塑多智能体系统,从原理落地到复杂任务规划实战
人工智能·算法
别动我齐刘海3 小时前
Day6 unitree_G1人形机器人GMR—— MotionInput
c语言·c++·人工智能·学习·机器学习·机器人·github
Hi李耶3 小时前
【LeetCode】541.反转字符串 II
算法·leetcode·职场和发展