Rust入门系列:05、常量、变量的可变性和shadowing特性
基本概念
在Rust编程语言中,变量的可变性指的是变量在声明后是否可以改变其值。Rust中的变量默认是不可变的,即一旦赋值后,不能更改其值。要使变量可变,必须使用 mut
关键字来声明。
让我们来看一个例子:
不可变变量(默认)
fn main() {
let x = 5;
println!("The value of x is: {}", x);
// x = 6; // 这行代码会导致编译错误,因为x是不可变的
}
在上述代码中,变量 x
被声明为不可变,所以试图更改 x
的值会导致编译错误。
可变变量
fn main() {
let mut y = 5;
println!("The value of y is: {}", y);
y = 6; // 这是允许的,因为y被声明为可变的
println!("The new value of y is: {}", y);
}
在这个示例中,变量 y
被声明为可变的(使用 mut
关键字),所以可以更改其值。
可变性与作用域
fn main() {
let x = 5;
{
let mut x = x; // 在这个作用域中,我们创建了一个新的可变变量x,它的初始值是外部x的值
x += 1;
println!("Inner x: {}", x);
}
println!("Outer x: {}", x); // 外部的x保持不变
}
在这个示例中,我们在内部作用域中创建了一个新的可变变量 x
,它与外部不可变的 x
是不同的变量。这展示了可变性在不同作用域中的应用。
常量
-
使用
const
关键字: 常量使用const
关键字定义。 -
必须显式指定类型: 常量必须显式指定其类型,不能通过类型推断。
-
值必须在编译时已知: 常量的值必须是一个常量表达式,即在编译时就能确定其值。
-
命名约定: 常量名通常使用全大写字母和下划线(UPPER_CASE)。
-
作用域: 常量可以在全局作用域定义,且在程序的整个生命周期内有效。
示例
const MAX_POINTS: u32 = 100_000;
const SECONDS_IN_MINUTE: u32 = 60;
const PI: f64 = 3.141592653589793;
fn main() {
println!("The maximum points are: {}", MAX_POINTS);
println!("Seconds in a minute: {}", SECONDS_IN_MINUTE);
println!("Value of Pi: {}", PI);
}
这个示例定义了三个常量:
MAX_POINTS
是一个无符号32位整数,值为 100_000。SECONDS_IN_MINUTE
是一个无符号32位整数,值为 60。PI
是一个64位浮点数,值为 3.141592653589793。
变量与常量的区别
在Rust中,常量和变量在定义、使用、作用域和特性上都有明显的区别。
1. 定义方式
- 变量:使用
let
关键字定义变量。默认情况下,变量是不可变的,但可以使用mut
关键字将其声明为可变变量。 - 常量:使用
const
关键字定义常量。常量必须显式指定类型,并且始终是不可变的。
2. 可变性
- 变量:如果使用
mut
关键字,变量可以在其生命周期内改变值。let mut x = 5; x = 6; // 允许的,因为x是可变的
- 常量:常量的值在定义后不能改变。
const MAX_POINTS: u32 = 100_000; // MAX_POINTS = 200_000; // 这行代码会导致编译错误,因为常量不可变
3. 作用域和生命周期
- 变量:变量的作用域是定义它的块或函数。当离开这个作用域时,变量会被释放。
fn main() { let x = 5; { let y = 10; // y在这个块内是有效的 } // y在这里无效 }
- 常量:常量在程序的整个生命周期内都有效。它们具有全局作用域,即使是在函数外部定义,也可以在整个程序中访问。
const GLOBAL_CONSTANT: u32 = 100; fn main() { println!("Global constant: {}", GLOBAL_CONSTANT); }
4. 运行时与编译时
- 变量:变量的值在运行时计算,可以依赖于程序的运行状态。
fn main() { let x = 5; let y = x + 2; // y的值在运行时计算 }
- 常量:常量的值在编译时必须是已知的,因此它们只能使用常量表达式来初始化。
const SECONDS_IN_MINUTE: u32 = 60; // const MINUTES_IN_HOUR: u32 = get_minutes(); // 这行代码会导致编译错误,因为函数调用不能用于常量初始化
5. 命名约定
- 变量:变量名通常使用蛇形命名法(snake_case)。
let my_variable = 10;
- 常量:常量名通常使用全大写字母和下划线(UPPER_CASE)。
const MAX_SIZE: u32 = 1000;
示例比较
fn main() {
let mut x = 5; // 可变变量
println!("The value of x is: {}", x);
x = 6;
println!("The new value of x is: {}", x);
const MAX_POINTS: u32 = 100_000; // 常量
println!("The maximum points are: {}", MAX_POINTS);
}
变量shadowing特性
在Rust中,变量的shadowing
是指在同一作用域或嵌套作用域内使用相同的变量名重新声明一个变量。这会导致新的变量遮蔽之前声明的同名变量,从而使得同名变量在新声明的变量生效期间不可见。遮蔽可以用于转换值类型或重新绑定变量,且遮蔽的变量不需要使用 mut
关键字。
代码示例
fn main() {
let x = 5;
println!("The value of x is: {}", x); // 输出: 5
let x = x + 1;
println!("The value of x is: {}", x); // 输出: 6
{
let x = x * 2;
println!("The value of x in the inner scope is: {}", x); // 输出: 12
}
println!("The value of x is: {}", x); // 输出: 6
}
解释
-
第一次声明
x
:let x = 5;
变量
x
被初始化为5
。 -
第一次遮蔽
x
:let x = x + 1;
这里新的
x
变量被声明,值为之前的x
加1
,即6
。此时,原来的x
被遮蔽。 -
在内层作用域中再次遮蔽
x
:{ let x = x * 2; println!("The value of x in the inner scope is: {}", x); // 输出: 12 }
在这个块内,再次声明了一个新的
x
,值为6 * 2
,即12
。这个新的x
仅在这个块的作用域内有效。 -
块结束后:
println!("The value of x is: {}", x); // 输出: 6
块结束后,内层作用域的
x
变量超出其生命周期,外层作用域的x
变量重新可见,值为6
。
shadowing的用途
-
重新绑定不可变变量: 遮蔽可以用于在同一作用域内重新绑定一个不可变变量,而不需要声明为可变变量。
let spaces = " "; let spaces = spaces.len();
-
类型转换: 遮蔽可以用于改变变量的类型,而不需要创建一个新的变量名。
let guess = "42"; let guess: u32 = guess.trim().parse().expect("Not a number!");
总结
变量可变性
- Rust中变量默认不可变,使用
mut
关键字声明可变变量。 - 可变变量可以在其生命周期内改变值。
变量作用域
- 变量的作用域是定义它的块或函数。
- 当离开作用域时,变量会被释放。
常量
- 使用
const
关键字定义常量。 - 常量必须显式指定类型,且始终不可变。
- 常量值在编译时必须是已知的。
- 常量在程序的整个生命周期内都有效。
变量与常量的区别
特性 | 变量 | 常量 |
---|---|---|
定义方式 | let 关键字 | const 关键字 |
可变性 | 可变或不可变 | 始终不可变 |
作用域 | 块或函数 | 程序全局 |
运行时与编译时 | 运行时计算 | 编译时必须已知 |
命名约定 | 蛇形命名法 | 全大写字母和下划线 |
变量shadowing
- 变量shadowing是指在同一作用域或嵌套作用域内使用相同的变量名重新声明一个变量。
- 新的变量会遮蔽之前声明的同名变量。
- shadowing可以用于转换值类型或重新绑定变量。
转载自:https://juejin.cn/post/7382892875111514147