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 哪个性能更好?为什么?

参考链接

返回首页 | 上篇 | 下篇

相关推荐
it小生01011 小时前
从底层思维到高效行动,顶尖人才进阶核心修炼指南
学习·学习方法
AI科技星1 小时前
基于全域数学公理体系的三元极值题最简求解法【乖乖数学】
线性代数·算法·游戏·决策树·机器学习·乖乖数学·全域数学
恣逍信点1 小时前
《凌微经》通俗解读——哲学第一因
学习·职场和发展·创业创新·学习方法·业界资讯·交友·哲学
大鱼>2 小时前
宠物异常行为预警系统:边缘计算与实时检测
人工智能·深度学习·算法·iot·宠物
red_redemption2 小时前
自由学习记录(207)
学习
AOwhisky2 小时前
Python 学习笔记(第四期)——字符串:格式化、操作与文本处理——核心知识点自测与详解
开发语言·笔记·python·学习·列表·元组·字典
apcipot_rain2 小时前
某双非机试真题
算法
光学补偿2 小时前
微服务学习 --- Spring Cloud概述
学习·spring cloud·微服务
米尔的可达鸭3 小时前
深入操作系统 Socket 底层:套接字控制块、FD映射、阻塞IO核心完整实现
arm开发·数据结构·websocket·网络协议·算法·架构·安全架构