Chapter 6 - 变量和作用域¶
6.1 作用域和遮蔽¶
变量绑定有一个作用域(scope),它被限定只在一个**代码块**(block)中生存(live)。 代码块是一个被 {} 包围的语句集合。另外也允许变量遮蔽。
fn main() {
// 此绑定生存于 main 函数中
let outer_binding = 1;
// 这是一个代码块,比 main 函数拥有更小的作用域
{
// 此绑定只存在于本代码块
let inner_binding = 2;
println!("inner: {}", inner_binding);
// 此绑定*遮蔽*了外面的绑定
let outer_binding = 5_f32;
println!("inner shadowed outer: {}", outer_binding);
}
// 代码块结束
// 此绑定仍然在作用域内
println!("outer: {}", outer_binding);
// 此绑定同样*遮蔽*了前面的绑定
let outer_binding = 'a';
println!("outer shadowed outer: {}", outer_binding);
}
执行结果:
6.2 不可变变量¶
在Rust中,你可以使用 mut 关键字来声明可变变量。可变变量与不可变变量相比,允许在绑定后修改它们的值。以下是一些常见的可变类型:
- 可变绑定(Mutable Bindings):使用
let mut声明的变量是可变的。这意味着你可以在创建后修改它们的值。例如:
- 可变引用(Mutable References):通过使用可变引用,你可以在不改变变量绑定的情况下修改值。可变引用使用
&mut声明。例如:
fn main() {
let mut x = 5;
modify_value(&mut x); // 通过可变引用修改x的值
println!("x: {}", x); // 输出 "x: 10"
}
fn modify_value(y: &mut i32) {
*y = 10;
}
- 可变字段(Mutable Fields):结构体和枚举可以包含可变字段,这些字段在结构体或枚举创建后可以修改。你可以使用
mut关键字来声明结构体或枚举的字段是可变的。例如:
struct Point {
x: i32,
y: i32,
}
fn main() {
let mut p = Point { x: 1, y: 2 };
p.x = 10; // 可以修改Point结构体中的字段x的值
}
- 可变数组(Mutable Arrays):使用
mut关键字声明的数组是可变的,允许修改数组中的元素。例如:
- 可变字符串(Mutable Strings):使用
String类型的变量和push_str、push等方法可以修改字符串的内容。例如:
这些是一些常见的可变类型示例。可变性是Rust的一个关键特性,它允许你在需要修改值时更改绑定,同时仍然提供了强大的安全性和借用检查。
6.3 可变变量¶
在Rust中,你可以使用 mut 关键字来声明可变变量。可变变量与不可变变量相比,允许在绑定后修改它们的值。以下是一些常见的可变类型:
- 可变绑定(Mutable Bindings):使用
let mut声明的变量是可变的。这意味着你可以在创建后修改它们的值。例如:
- 可变引用(Mutable References):通过使用可变引用,你可以在不改变变量绑定的情况下修改值。可变引用使用
&mut声明。例如:
fn main() {
let mut x = 5;
modify_value(&mut x); // 通过可变引用修改x的值
println!("x: {}", x); // 输出 "x: 10"
}
fn modify_value(y: &mut i32) {
*y = 10;
}
- 可变字段(Mutable Fields):结构体和枚举可以包含可变字段,这些字段在结构体或枚举创建后可以修改。你可以使用
mut关键字来声明结构体或枚举的字段是可变的。例如:
struct Point {
x: i32,
y: i32,
}
fn main() {
let mut p = Point { x: 1, y: 2 };
p.x = 10; // 可以修改Point结构体中的字段x的值
}
- 可变数组(Mutable Arrays):使用
mut关键字声明的数组是可变的,允许修改数组中的元素。例如:
- 可变字符串(Mutable Strings):使用
String类型的变量和push_str、push等方法可以修改字符串的内容。例如:
这些是一些常见的可变类型示例。可变性是Rust的一个关键特性,它允许你在需要修改值时更改绑定,同时仍然提供了强大的安全性和借用检查。
6.4 语句(Statements),表达式(Expressions) 和 变量绑定(Variable Bindings)¶
6.4.1 语句(Statements)¶
Rust 有多种语句。在Rust中,下面的内容通常被视为语句:
- 变量声明语句,如
let x = 5;。 - 赋值语句,如
x = 10;。 - 函数调用语句,如
println!("Hello, world!");。 - 控制流语句,如
if、else、while、for等。
fn main() {
// 变量声明语句
let x = 5;
// 赋值语句
let mut y = 10;
y = y + x;
// 函数调用语句
println!("The value of y is: {}", y);
// 控制流语句
if y > 10 {
println!("y is greater than 10");
} else {
println!("y is not greater than 10");
}
}
6.4.2 表达式(Expressions)¶
在Rust中,语句(Statements)和表达式(Expressions)有一些重要的区别:
- 返回值:
- 语句没有返回值。它们执行某些操作或赋值,但不产生值本身。例如,赋值语句
let x = 5;不返回任何值。 -
表达式总是有返回值。每个表达式都会计算出一个值,并可以被用于其他表达式或赋值给变量。例如,
5 + 3表达式返回值8。 -
可嵌套性:
- 语句可以包含表达式,但不能嵌套其他语句。例如,
let x = { 5 + 3; };在代码块中包含了一个表达式,但代码块本身是一个语句。 -
表达式可以包含其他表达式,形成复杂的表达式树。例如,
let y = 5 + (3 * (2 - 1));中的表达式包含了嵌套的子表达式。 -
使用场景:
- 语句通常用于执行某些操作,如声明变量、赋值、执行函数调用等。它们不是为了返回值而存在的。
-
表达式通常用于计算值,这些值可以被用于赋值、函数调用的参数、条件语句的判断条件等。它们总是有返回值。
-
分号:
- 语句通常以分号
;结尾,表示语句的结束。 - 表达式也可以以分号
;结尾,但这样做通常会忽略表达式的结果。如果省略分号,表达式的值将被返回。
下面是一些示例来说明语句和表达式之间的区别:
// 这是一个语句,它没有返回值
let x = 5;
// 这是一个表达式,它的值为 8
let y = 5 + 3;
// 这是一个语句块,其中包含了两个语句,但没有返回值
{
let a = 1;
let b = 2;
}
// 这是一个表达式,其值为 6,这个值可以被赋给变量或用于其他表达式中
let z = {
let a = 2;
let b = 3;
a + b // 注意,没有分号,所以这是一个表达式
};
再来看一下,如果给表达式强制以分号 ; 结尾的效果。
fn main() {
//变量绑定, 创建一个无符号整数变量 `x`
let x = 5u32;
// 创建一个新的变量 `y` 并初始化它
let y = {
// 创建 `x` 的平方
let x_squared = x * x;
// 创建 `x` 的立方
let x_cube = x_squared * x;
// 计算 `x_cube + x_squared + x` 并将结果赋给 `y`
x_cube + x_squared + x
};
// 代码块也是表达式,所以它们可以用作赋值中的值。
// 这里的代码块的最后一个表达式是 `2 * x`,但由于有分号结束了这个代码块,所以将 `()` 赋给 `z`
let z = {
2 * x;
};
// 打印变量的值
println!("x is {:?}", x);
println!("y is {:?}", y);
println!("z is {:?}", z);
}
返回的是
总之,语句用于执行操作,而表达式用于计算值。理解这两者之间的区别对于编写Rust代码非常重要。