鸠山一茶 发表于 2024-4-2 15:38

【Rust】初窥默认变量不可变与变量遮蔽

#### 很早之前就打算学习一下Rust,写下第一篇帖子记录学习过程,主要是通过简单的代码示例探索一些Rust的特性

##### 对于`默认变量不可变`这个问题,从底下的两段代码可以看出来

```rust
fn main() {
    let x = 5;
    println!("The value of x is: {}", x);
    x = 6;
    println!("The value of x is: {}", x);
}
```

**在这里运行的时候就会报错**

```rust
error: cannot assign twice to immutable variable `x`
--> src\main.rs:4:5
|
2 |   let x = 5;
|         -
|         |
|         first assignment to `x`
|         help: consider making this binding mutable: `mut x`
3 |   println!("The value of x is: {x}");
4 |   x = 6;
|   ^^^^^ cannot assign twice to immutable variable
```

**这里就是标识这里对于变量x发生了改变,想要对x进行第二次赋值,但是x默认是不可变的变量,因此编译器无法通过编译,造成了报错的情况发生。**

```rust
fn main() {
    let mut x = 5;
    println!("The value of x is: {}", x);
    x = 6;
    println!("The value of x is: {}", x);
}
```

**在这段代码当中,我们通过添加**

```rust
let mut
```

**使得这个变量可变,从而完成对于变量x的赋值,假设我们在下面再次进行赋值**

```rust
fn main() {
    let mut x = 5;
    println!("The value of x is: {}", x);
    x = 6;
    println!("The value of x is: {}", x);
    x = 7;
    println!("The value of x is: {}", x);
}
```

**也是可以正常的进行编译与输出的**

```rust
The value of x is: 5
The value of x is: 6
The value of x is: 7
```

**下面再来进行一个更有趣的实验,我们来尝试一下下面的代码**

```rust
fn main() {
    let mut x = 5;
    println!("The value of x is: {}", x);
    let x = 6;
    println!("The value of x is: {}", x);
    x = 7;
    println!("The value of x is: {}", x);
}
```

**现在不是能够正常进行编译的**

```rust
error: cannot assign twice to immutable variable `x`
--> src\main.rs:6:5
|
4 |   let x = 6;
|         -
|         |
|         first assignment to `x`
|         help: consider making this binding mutable: `mut x`
5 |   println!("The value of x is: {}", x);
6 |   x = 7;
|   ^^^^^ cannot assign twice to immutable variable
```

### 为什么会出现这种错误呢?

答案是Rust特性在这里被使用了,那就是**变量遮蔽(Shadowing)**。
当使用`let`关键字再次声明同名的变量时,新的变量会遮蔽(或隐藏)之前的变量。这是允许的,即使原来的变量是可变的,使用`let`再次声明同名变量并不需要`mut`关键字,因为实际上是在创建一个新的变量。
这里的关键点是,遮蔽允许你重新使用同一个名称,但每次使用`let`都会创建一个新的、独立的变量。

再来看代码:

```rust
fn main() {
    let mut x = 5;
    println!("The value of x is: {}", x); // 这里 x 是 5
    let x = 6;
    println!("The value of x is: {}", x); // 现在新的 x 遮蔽了旧的 x,这里 x 是 6
    x = 7; // 编译错误:因为这里的 x 是不可变的
    println!("The value of x is: {}", x);
}
```

在第二次使用`let x = 6;`时,实际上创建了一个新的不可变的变量`x`,它遮蔽了之前的可变变量`x`。因此,当你尝试执行`x = 7;`时,会遇到编译错误,因为这个新的`x`是不可变的。

**如何修复这个错误?**

如果想在之后修改`x`的值,需要确保`x`是可变的。
但是,如果想继续使用变量遮蔽(而不是保持原变量的可变性),只能对这个新的遮蔽变量重新赋值通过再次使用`let`,如下所示:

```rust
fn main() {
    let mut x = 5;
    println!("The value of x is: {}", x); // 使用 {} 来插值变量
    let x = 6;
    println!("The value of x is: {}", x); // x 被遮蔽,现在是 6
    let x = 7; // 再次遮蔽 x,现在 x 是 7
    println!("The value of x is: {}", x);
}
```

这样的代码完全有效,它在这里展示了变量遮蔽的能力,允许重新绑定一个变量名给一个新的值,即使新旧变量的可变性不同。

塞北的雪 发表于 2024-4-2 16:16

那这个功能就不能叫变量,直接叫定义常量多直白

IPIRATEXAPTAIN 发表于 2024-4-2 17:07


那这个功能就不能叫变量,直接叫定义常量多直白

kittylang 发表于 2024-4-2 18:03

本帖最后由 kittylang 于 2024-4-2 18:06 编辑

塞北的雪 发表于 2024-4-2 16:16
那这个功能就不能叫变量,直接叫定义常量多直白
常量是常量,变量是变量,常量会被编译进可执行文件,变量被放入内存{:1_925:}

编译时你就知道常量写的啥,要是我变量是用户输入呢,你知道他是啥吗

go4399 发表于 2024-4-2 20:46

let mut = var
let = const var

鸠山一茶 发表于 2024-4-3 12:52

塞北的雪 发表于 2024-4-2 16:16
那这个功能就不能叫变量,直接叫定义常量多直白

变量和常量这俩根本就不是同一个玩意啊,常量是一个固定的值,Rust只是设计为默认情况下变量不能更改,但是变量在添加let mut以后也是可以被修改的,如果是常量岂不是直接就写死了

鸠山一茶 发表于 2024-4-3 12:59

本帖最后由 鸠山一茶 于 2024-4-3 13:01 编辑

IPIRATEXAPTAIN 发表于 2024-4-2 17:07
那这个功能就不能叫变量,直接叫定义常量多直白#### 再者说如果这个变量是用户的本地mac值那我怎么在写代码的时候定义成常量

```rust
fn main() {
   // 我定义了一个空的MAC地址
    let mut user_mac_address = " ".to_string();
   
    println!("当前用户的 MAC 地址是: {}", user_mac_address);

    // 我在这里获取了用户的MAC地址,更新了变量
    user_mac_address = "00:0a:95:9d:68:17".to_string();
   
    println!("更新后用户的 MAC 地址是: {}", user_mac_address);
}
```
#### 如果我下面再写一个加密逻辑,用的是用户MAC地址来计算,那要是定义成常量岂不是大家全是一样的了

#### 常量是需要在整个生命周期都不会改变的值才能定义为常量写死在程序里面,动态信息写成常量?

忘了带钱 发表于 2024-4-7 09:34

主要要理解rust 的内存模型和所有权的概念,变量移动之后所有权变更,除非类型实现了 copy trait,我感觉就是把C++里的智能指针用所有权的方式在编译阶段帮你实现了... 不过一开始使用起来就很不习惯

鸠山一茶 发表于 2024-4-8 14:27

忘了带钱 发表于 2024-4-7 09:34
主要要理解rust 的内存模型和所有权的概念,变量移动之后所有权变更,除非类型实现了 copy trait,我感觉就 ...

可以改掉不少坏的习惯,提升代码质量还是不错的:loveliness:
页: [1]
查看完整版本: 【Rust】初窥默认变量不可变与变量遮蔽