我有一个与Rust中的移动语义有关的问题。 据我了解,Rust中的“ mut”关键字是要使一些变量 可变的,即可变变量可以再次绑定到另一个值;但是,此可变性仅用于绑定。因此,如果我真的想更改变量的值,则应使用“&mut”关键字,如下所示:
let mut two = 2;
let t = &mut two;
*t += 1;// update the value of two, not only bind t to another values
print!("{}", t); // here, t is 3
但是,在使用结构的情况下,似乎并非如此。
这是示例代码(https://doc.rust-lang.org/book/ch05-01-defining-structs.html):
let mut user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
user1.email = String::from("anotheremail@example.com");
为什么我可以重写user1的“电子邮件”字段? 似乎不像是对user1的重新绑定。
答案 0 :(得分:1)
我认为您的问题源于mut
作为关键字的一小部分困惑,类推可能会有所帮助。
想象一下,我拥有一辆汽车。实际上,让我们首先定义一下汽车是什么:
pub struct Car {
fuel: usize,
pub color: String,
pub wheel_count: u8
}
让我们定义我的车
let mut my_car:Car = Car { fuel: 100, color: "Green".to_string(), wheel_count: 4 };
这是我的车。我已将其定义为可变实体(let mut
),因此,如果需要,可以加油并将其变成蓝色
my_car.fuel += 20;
my_car.color = "Blue".to_string();
这取决于变量本身的定义。如果我只有let
我的车,那我就做不到。为变量分配let mut
表示,无论谁具有以下条件,均可在所有字段中对其进行修改:
然后我决定去加油站加油。我把车借给服务员,以便他们为我做:
pub fn lend_car_to_attendant(target_car: &mut Car) {
target_car.fuel += 20;
}
如果整辆车是他的临时工,他可能会开车去油漆厂并改变汽车的颜色。还值得注意的是,虽然他有车,但我不能用它做任何事。我把它借给了他,直到他退回borrow
为止,就是他。
现在,当然,在有车时间之外,任何人都可以窥视我的车并欣赏它的颜色(或它的车轮数量)。拥有不可变借项的任何人都可以公开检查my_car
的所有公共属性。
然后我决定尝试另一个加油站,事实证明这是不诚实的:
pub fn lend_car_to_naughty_attendant(target_car: &mut Car) {
target_car.fuel += 20;
lend_car_to_paint_shop(target_car);
}
pub fn lend_car_to_paint_shop(target_car: &mut Car) {
target_car.color = "Bubblegum Pink".to_string();
}
我的车回来了泡泡糖粉红色!
我们可以通过有人监视汽车来避免这种情况。如果我们向&my_car
借钱,然后再次尝试去那个糟糕的加油站,程序将根本无法编译(example here)
答案 1 :(得分:0)
我认为您对变量的工作方式感到困惑。变量就像可以存储数据的杯子,对于像锈这样的强类型语言,变量只能存储一种数据。
因此,当您声明一个值时,基本上是在指示计算机在内存中分配一些空间:
let x: u32;
在上面的示例中,基本上您是在说“计算机,给我一个足够大的杯子来容纳u32数据(32 bits)”。
然后计算机为您提供杯子,在本例中,x是所有者。 x
是您握住杯子的方式,换句话说,是存储空间的所有者。
现在让我们用适当的数据填充杯子:
x = 42;
一旦强大的计算机为x保留了该杯子,它就属于x,直到x放弃所有权:
{
let x: u32 = 42;
let y = x;
}
{
let x: u32 = 42;
drop(x);
}
{
let x: u32 = 42;
}
// x is dropped here
释放内存空间后,计算机可以将其分配给其他变量。
您可以如下检查内存空间的地址:
println!("address of var: {:p}", &x);
在初始化x时,计算机为您提供该存储空间。这是正在工作的资源获取初始化(RAII):
fn main() {
let x: u32;
println!("address of x: {:p}", &x);
x = 12;
println!("{}", x);
}
您收到编译器错误:
12 | println!("address of x: {:p}", &x);
| ^^ use of possibly uninitialized `x`
但是,如果您在初始化后检查地址,它将编译而不会出现任何错误:
fn main() {
let x: u32;
x = 12;
println!("address of x: {:p}", &x); // address of x: 0x7ffc8183402c
}
声明变量时,您与编译器有合同。 mut
是该合同的条款之一。
通过不使用mut
关键字,您只是说一旦填充(初始化变量),就永远不会更改杯中的内容(在内存空间x点)。一旦您加满杯子,它就会保持这种状态。
但是如果您使用mut
关键字,合同会说您可以在该内存空间中放入任何值,只要它是正确的类型即可。
在两种情况下,该存储空间的所有者均为x。可变性与所有权无关。
现在,关于您的示例:
let mut two = 2;
let t = &mut two;
*t += 1;
在第二行中,您将对变量two
进行可变引用。换句话说,t借用x指向的内容。在下一行中,将3填充到杯子中。由于您正在使用可变引用,因此您必须在下一行*t += 1;
中取消引用。
let mut two = 2;
println!("address of &two: {:p}", &two);
let t = &mut two;
*t += 1;
println!("address of t: {:p}", t);
此打印:
address of &two: 0x7ffc5869c9c4
address of t : 0x7ffc5869c9c4
two
是所有者,t只是借来的:
let mut two = 2;
{
let t = &mut two;
*t += 1;
println!("{:?}", t);
}
two += 10;
println!("{:?}", two);
再次的可变性与所有权无关,Rust限制了借用可变值以消除混乱,因为当多个变量可以改变杯子中的内容时,很容易失去对谁做什么的控制。
对于用户类型,如果将user1
值初始化为可变值,则可以更改其属性中存储的内容。就像持有其他杯子的杯子,或指向堆中存储的其他杯子的指针一样。