关于如何改变结构字段的Rust book:
let mut point = Point { x: 0, y: 0 };
point.x = 5;
以后:
可变性是绑定的属性,而不是结构本身。
这对我来说似乎是违反直觉的,因为point.x = 5
看起来并不像重新绑定变量point
。有没有办法解释这个,所以它更直观?
我能绕过这一点的唯一方法就是“想象”我正在将point
重新绑定到原始Point
的副本,其值x
不同(不是甚至确定这是准确的。)
答案 0 :(得分:10)
这对我来说似乎是违反直觉的,因为point.x = 5看起来并不像我正在重新定义变量点。有没有办法解释这个,所以它更直观?
所有这一切都在说,某个东西是否可变是由变量的let
- 语句(绑定)决定的,而不是该类型或任何特定字段的属性。
在示例中,point
及其字段是可变的,因为在point
语句中引入了let mut
(而不是简单的let
语句)而不是因为一般来说Point
类型的某些属性。
作为对比,要说明为什么这很有趣:在其他语言中,如OCaml,您可以在类型的定义中标记某些字段是可变的:
type point =
{ x: int;
mutable y: int;
};
这意味着您可以改变每个y
值的point
字段,但您永远不能改变x
。
答案 1 :(得分:7)
这里“绑定”不是动词,而是名词。你可以说Rust绑定是变量的同义词。因此,您可以阅读该段落
可变性是变量的属性,而不是结构本身的属性。
现在,我想,应该很清楚 - 您将变量标记为可变,因此您可以修改其内容。
答案 2 :(得分:6)
我也有同样的困惑。对我来说,它来自两个不同的误解。首先,我来自一种语言,其中变量(也称为绑定)隐含地引用了值。在该语言中,区分变异引用和改变所引用的值是很重要的。其次,我认为"结构本身"这本书是指实例化的价值,但通过"结构"它表示规范/声明,而不是该类型的特定值。
Rust中的变量是不同的。来自reference:
变量是堆栈帧的一个组成部分......
局部变量(或堆栈本地分配)直接保存值, 在堆栈的内存中分配。该值是堆栈的一部分 帧。
因此,变量是堆栈帧的一个组件 - 一块内存 - 直接保存该值。没有提及区分值本身,也没有提到mutate。变量和值是相同的内存块。
结果是,在更改变量的意义上重新绑定变量以引用不同的内存块与Rust的内存模型不兼容。 (n.b。let x = 1; let x = 2;
创建两个变量。)
所以这本书指出,在每块大小的内存中声明可变性"级别而不是结构定义的一部分。
我能绕过这个问题的唯一方法就是"想象"那是我 重新绑定指向具有不同x的原始Point的副本 价值(甚至不确定是否准确)
相反,想象一下你正在将一块内存中的0中的一个更改为5;并且该值位于point
指定的内存中。解释"绑定是可变的"表示您可以改变绑定指定的内存块,包括仅改变其中的一部分,例如,通过设置结构字段。考虑以Rust描述的方式重新绑定Rust变量。
答案 3 :(得分:5)
@ m-n的回答让我走上正轨。这都是关于堆栈地址的!这是一个在我脑海中凝固了实际情况的演示。
cvsVehicleReadResponse
输出(每次运行的地址明显不同):
struct Point {
x: i64,
y: i64,
}
fn main() {
{
println!("== clobber binding");
let a = 1;
println!("val={} | addr={:p}", a, &a);
// This is completely new variable, with a different stack address
let a = 2;
println!("val={} | addr={:p}", a, &a);
}
{
println!("== reassign");
let mut b = 1;
println!("val={} | addr={:p}", b, &b);
// uses same stack address
b = 2;
println!("val={} | addr={:p}", b, &b);
}
{
println!("== Struct: clobber binding");
let p1 = Point{ x: 1, y: 2 };
println!(
"xval,yval=({}, {}) | pointaddr={:p}, xaddr={:p}, yaddr={:p}",
p1.x, p1.y, &p1, &p1.x, &p1.y);
let p1 = Point{ x: 3, y: 4 };
println!(
"xval,yval=({}, {}) | pointaddr={:p}, xaddr={:p}, yaddr={:p}",
p1.x, p1.y, &p1, &p1.x, &p1.y);
}
{
println!("== Struct: reassign");
let mut p1 = Point{ x: 1, y: 2 };
println!(
"xval,yval=({}, {}) | pointaddr={:p}, xaddr={:p}, yaddr={:p}",
p1.x, p1.y, &p1, &p1.x, &p1.y);
// each of these use the same addresses; no new addresses
println!(" (entire struct)");
p1 = Point{ x: 3, y: 4 };
println!(
"xval,yval=({}, {}) | pointaddr={:p}, xaddr={:p}, yaddr={:p}",
p1.x, p1.y, &p1, &p1.x, &p1.y);
println!(" (individual members)");
p1.x = 5; p1.y = 6;
println!(
"xval,yval=({}, {}) | pointaddr={:p}, xaddr={:p}, yaddr={:p}",
p1.x, p1.y, &p1, &p1.x, &p1.y);
}
}
关键点是:
== clobber binding
val=1 | addr=0x7fff6112863c
val=2 | addr=0x7fff6112858c
== reassign
val=1 | addr=0x7fff6112847c
val=2 | addr=0x7fff6112847c
== Struct: clobber binding
xval,yval=(1, 2) | pointaddr=0x7fff611282b8, xaddr=0x7fff611282b8, yaddr=0x7fff611282c0
xval,yval=(3, 4) | pointaddr=0x7fff61128178, xaddr=0x7fff61128178, yaddr=0x7fff61128180
== Struct: reassign
xval,yval=(1, 2) | pointaddr=0x7fff61127fd8, xaddr=0x7fff61127fd8, yaddr=0x7fff61127fe0
(entire struct)
xval,yval=(3, 4) | pointaddr=0x7fff61127fd8, xaddr=0x7fff61127fd8, yaddr=0x7fff61127fe0
(individual members)
xval,yval=(5, 6) | pointaddr=0x7fff61127fd8, xaddr=0x7fff61127fd8, yaddr=0x7fff61127fe0
来" clobber"现有绑定(新堆栈地址)。即使变量被声明为let
,也会发生这种情况,所以要小心。mut
重复使用现有的堆栈地址,但在重新分配时不要使用mut
。这项测试揭示了一些有趣的事情: