paint-brush
可变引用还是可变变量?解码 Rust 的 mut-mut-mut经过@charnog
883 讀數
883 讀數

可变引用还是可变变量?解码 Rust 的 mut-mut-mut

经过 Denis Gonchar5m2023/09/23
Read on Terminal Reader

太長; 讀書

Rust 的内存安全植根于其所有权和借用机制。本文深入研究 mut 关键字的复杂性,区分可变引用和可变变量。关键要点是什么?并非所有 mut 都是一样的:可变引用允许更改指向的值,而带有引用的可变变量可以更改它们指向的位置。使用 = 符号的便捷可视化技巧可以简化对这些区别的理解。
featured image - 可变引用还是可变变量?解码 Rust 的 mut-mut-mut
Denis Gonchar HackerNoon profile picture

介绍

尽管 Rust 提供了 Rust 书籍,但我还是很难理解mut关键字。我问自己:“我是唯一一个有这个问题的人吗?”谷歌的快速搜索证实我并不孤单。


因此,我决定写这篇文章来详细解释mut关键字。本文适用于那些使用过 Python 或 JavaScript 等高级语言的人。

变量

要在 Rust 中创建不可变变量,只需编写let x = 1337 。这很简单。如果你想创建一个可以稍后修改的变量,只需在let后面添加mut关键字即可。 Rust 有一个有用的约定,鼓励明确意图。


添加mut关键字会通知其他人该变量将在代码中的其他位置进行修改。好的。


让我们想象一下。这里有两个变量, let mut x = 1337let y = 42

名;第二个 - 类型,第三个 - 值,第四个 - mut 标志。

参考文献

目前,一切都很简单。然而,当使用mut引用时,事情开始变得有点复杂。让我们创建一些。

 let mut x = 1337; let y = 42; let x_ref = &mut x; let y_ref = &y;


我创建了两个引用(或者用 Rust 的术语“借用”)。其中一个是可变引用,另一个是只读引用。让我们再次为此创建一个方案。


在给定的方案中,我有 4 个变量,其中 2 个是引用。两个引用变量都是不可变的,并且在let之后没有mut关键字,这意味着我无法更改它们指向的内容。但是,我仍然可以更改它们引用的值。

 *x_ref = 777;


如果你这样写,Rust 编译器不会抱怨,并且x的值(不是 ref 本身)将更改为777 。然而,该方案上有一个红色方块,表明x_ref缺少可变性选项。那么,为什么我可以改变它所指的值呢?


让我们回到let x_ref = &mut x的方案。


第一个白色块包含名称: x_ref 。第二个告诉我该变量中存储的类型。在其完整形式中,没有任何隐式类型注释,我可以编写如下:

 let x_ref: &mut i32 = &mut x;


我可以将其解释为:让我们创建一个名为x_ref不可变变量,它将保存对i32可变引用,并立即使用x变量中对i32值的可变引用对其进行初始化。


这意味着我可以修改它指向的值,但无法更改引用的值(或地址)。换句话说,我不能写这样的东西:

 let x_ref: &mut i32 = &mut x; let mut z = 0; x_ref = &mut z; // Not allowed!


就方案而言,我想更改上面代码块中箭头指向的方向。但是,即使z变量是可变的,我也无法更改箭头,因为问题在于x_ref本身的不可变性。


要更改箭头方向,我需要修改x_ref变量中存储的地址。但是,我不能这样做,因为变量是不可变的。


我们开始做吧!

 let mut x: i32 = 1337; let mut x_ref: &mut i32 = &mut x; // I've added mut before x_ref let mut z = 0; x_ref = &mut z; // Allowed!


x_ref周围有太多mut实例,对吗?让我们来描述一下它们。


  1. let mut x_ref :我正在创建一个名为x_ref的可变变量,这意味着我可以稍后更改它的值。


  2. &mut i32 :我声明该变量将包含对i32类型的某些值的可变引用。


  3. &mut x :我借用(引用)变量x


然后,我创建了一个名为z的变量并为其指定了值0 。后来,当我写x_ref = &mut z时,我表示我理解x_ref是一个可变变量,只能保存对i32值的引用。


由于z的类型是i32 ,我可以将其地址分配给x_ref变量。为了获取z的地址,我使用了&mut z语法。


方案。

心理诡计

看看语句中的= ,可能看起来有点明显,但是……

 let mut x_ref = &mut x;


…我将其视为一个分隔符(特别是如果将其旋转 90 度),将语句分为两个子语句:左和右。左侧提供有关变量本身的信息,而右侧则告诉我们有关值的信息


当我使用*取消引用运算符来更改值时...

 *x_ref = 100;

...我不更改x_ref变量的值。相反,我正在更改x_ref引用的值。

不可变的引用

我以前经常使用mut 。如果我省略其中一些怎么办?

 let i = 1; let j = 2; let mut k = &i;


我可以改变这里的i值吗?使用分隔符技术,回答起来非常简单。我可以更改k的值(我在左侧看到mut ),但值(右侧)是对i不可变引用(这里没有mut )。


所以…

 let i = 1; let j = 2; let mut k = &i; k = &j; // This is legal. *k = 3; // This is not.


方案。

结论

在本文中,我们剖析了mut关键字和引用的细微差别。请记住,可变引用持有引用的可变变量之间是有区别的。我们的伎俩?


使用=符号作为心理分隔符可以更好地理解 Rust 中的赋值。这个简单的可视化可以消除许多困惑。


快乐编码!