Rust 中变量与可变性
1. 不可变变量(Immutable Variables)
1.1 为什么默认不可变?
Rust 之所以默认使用不可变变量,是为了让你的代码更安全、更易于理解,并且能更好地利用 Rust 在并发编程中的优势。当你声明一个不可变变量后,就不需要再担心它会在其它地方被意外修改,从而避免很多隐藏的逻辑错误。
下面演示一个例子,请先创建一个名为 variables
的新项目:
cargo new variables
进入该项目后,将 src/main.rs
的内容替换为以下代码:
fn main() {
let x = 5;
println!("The value of x is: {}", x);
x = 6; // 这里会导致编译错误
println!("The value of x is: {}", x);
}
然后执行:
cargo run
你会看到一个类似下面的错误信息:
error[E0384]: cannot assign twice to immutable variable `x`
--> src/main.rs:4:5
|
2 | let x = 5;
| -
| |
| first assignment to `x`
3 | println!("The value of x is: {}", x);
4 | x = 6;
| ^^^^^ cannot assign twice to immutable variable
报错信息说明你无法为一个不可变变量重新赋值。Rust 通过编译时的强制保证了只要你声明了不可变变量,那么它的值就不会再发生变化。如果某处逻辑假设变量不变,而另一处却意外改变了它,这往往会带来难以排查的 bug。Rust 编译器在编译期就会发现并阻止这种用法,让我们的代码更加可靠。
2. 可变变量(Mutable Variables)
2.1 使用 mut
关键字
虽然默认是不可变,但在实际开发中,我们常常需要能够修改变量的值。只需在声明时加上 mut
关键字,就能将变量变为可变。比如:
fn main() {
let mut x = 5;
println!("The value of x is: {}", x);
x = 6;
println!("The value of x is: {}", x);
}
再次执行 cargo run
,现在这段代码可以正常运行并输出:
The value of x is: 5
The value of x is: 6
因为使用了 mut
,同一个变量可以被重新赋值。是否使用 mut
完全取决于你想要的代码语义:如果你需要改变变量的值,就用 mut
;如果希望它保持不变,就使用默认的不可变声明。
3. 常量(Constants)
3.1 常量与不可变变量的区别
在 Rust 中,常量也是绑定到一个名称上的值,而且它们同样不可变。但与不可变变量相比,常量有以下不同点:
- 声明方式:常量使用
const
关键字,而不是let
。 - 类型标注:常量必须显式标注类型。
- 不可使用
mut
:常量是永远不可变的,不能使用mut const
。 - 作用域:常量可以在任何作用域(包括全局作用域)中声明,且在其作用域内始终有效。
- 赋值限制:常量只能被设置为编译时就能确定的值(编译期常量表达式),不能依赖运行时计算的结果。
下面是一个常量声明示例:
const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;
这里的 THREE_HOURS_IN_SECONDS
会在整个程序的生命周期内有效,而且它表示的值在编译期就被确定。
在实际项目里,常量通常用来表示不会改变的、在任何地方都可能被引用的、具有固定意义的数值,比如游戏中的最大得分、物理公式中的光速等。
4. 遮蔽(Shadowing)
4.1 什么是遮蔽?
在 Rust 中,如果再次使用 let
声明同名变量,后一个变量会遮蔽(shadow)掉前一个变量。这样做会在同一个作用域下复用相同的变量名,但在编译时后声明的变量会覆盖(shadow)先前的变量。例如:
fn main() {
let x = 5;
let x = x + 1; // 这里重新声明 x,将原先的值 5 + 1 = 6
{
let x = x * 2; // 在一个新的作用域里再次声明 x => 6 * 2 = 12
println!("The value of x in the inner scope is: {}", x); // 输出 12
}
println!("The value of x is: {}", x); // 离开内层作用域,这里再次是 x = 6
}
输出结果为:
The value of x in the inner scope is: 12
The value of x is: 6
可以看出:
- 在外层作用域中,第一次
let x = 5;
将 x 初始化为 5。 - 第二次
let x = x + 1;
中,x 变为 6(此时原先的 x 被遮蔽)。 - 在内层作用域
{ ... }
中,第三次let x = x * 2;
,x 又变为 12(再次遮蔽前面的 x)。 - 离开内层作用域后,内层声明的 x(值为 12)不再可见,x 又回到值 6。
4.2 遮蔽与 mut
的区别
- 使用
mut
:你可以对同一个变量直接进行赋值操作,而不需要再次使用let
。 - 使用遮蔽:每次都是声明一个新的不可变变量,后者会覆盖(shadow)前者的作用域内引用。
一个显著的区别是,如果要改变变量的类型,只能通过再次使用 let
(即遮蔽),而不能单纯用 mut
。例如:
fn main() {
// 第一版:遮蔽(允许改变类型)
let spaces = " "; // 这里是字符串类型
let spaces = spaces.len(); // 这里是数字类型
println!("spaces 的值是: {}", spaces); // 输出 3
// 第二版:仅用 mut(编译报错示例)
let mut other_spaces = " "; // 字符串类型
other_spaces = other_spaces.len(); // 尝试改为数字类型,编译器会报错
}
对于第二段代码,编译器会提示类似 “expected &str
, found usize
” 的错误,即无法对同一个 mut
变量直接变更其类型。这也体现了 Rust 类型系统的严格与安全:一旦使用了某种类型声明,mut
只允许你改变该值的内容,而不是值本身的类型。
5. 总结
- 变量默认不可变:可以提高代码的安全性,避免无意间的修改带来的潜在 bug。
- 可变变量(
mut
):在确实需要的地方使用可变变量,让代码更灵活,并明确告诉读者这个变量会被修改。 - 常量(
const
):- 不能被修改,且必须显式标注类型;
- 只能设置为编译期能确定的值;
- 在整个作用域内都有效,常用于全局不可变值。
- 遮蔽(Shadowing):
- 使用
let
重新声明同名变量会覆盖(shadow)之前声明的变量; - 可以改变变量类型而无需修改变量名称;
- 遮蔽与
mut
在实际使用场景和语义上都有不同。
- 使用
通过对这些概念的深入理解,可以在 Rust 中更灵活地管理变量、避免潜在 bug,并写出更健壮、清晰的程序。Rust 的编译器对我们开发者有很强的约束力——虽然有时可能会觉得“麻烦”,但正是这种严格保证了程序的安全性和可维护性。希望这篇博客能帮助你在日常编写 Rust 代码时,更加自信地使用不可变变量和可变变量,并且在正确的时候选择使用常量或遮蔽。祝你学习愉快,写出更优雅、更安全的 Rust 程序!
原文地址:https://blog.csdn.net/weixin_43114209/article/details/145245885
免责声明:本站文章内容转载自网络资源,如侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!