我在理解Rust中的ref
模式时遇到了问题。我指的是https://rustbyexample.com/scope/borrow/ref.html
以下是我不理解的代码:
let point = Point { x: 0, y: 0 };
let _copy_of_x = {
// `ref_to_x` is a reference to the `x` field of `point`
let Point { x: ref ref_to_x, y: _ } = point;
// Return a copy of the `x` field of `point`
*ref_to_x
};
我得到的是最后一个let
表达式(?)是某种模式匹配。因此,我的理解ref ref_to_x
应该等于0
,即原始x
的{{1}}值。
但我不明白point
实际上做了些什么。当我添加这样的代码时:
ref
我总是得到println!("x: {}", point.x);
println!("ref_to_x: {}", ref_to_x);
println!("*ref_to_x: {}", *ref_to_x);
,所以似乎没有区别。不知怎的,我期望0
的内存地址,而ref_to_x
可能再次被取消引用。
我可以用*ref_to_x
替换ref ref_to_x
和*ref_to_x
,代码仍然可以使用。有什么不同? myx
到底做了什么?
编辑:在阅读dbaupps回答并对ref
和ref_to_x
进行一些添加后,事情变得更加清晰;你不能将一个整数添加到*ref_to_x
,因为它是一个引用。我想我很困惑,因为在打印时没有任何参考标记。
答案 0 :(得分:15)
使用ref
创建的引用与使用&
的引用完全相同。
区别在于它们在语法中被允许的位置。作业左侧的ref
就像在右侧添加&
一样。
这些表达式是等效的:
let ref x1 = y;
let x2 = &y;
这种冗余的存在是因为模式匹配&
用于要求已经存在引用,而不是创建一个新引用:
let foo = 1;
match foo {
ref x => {
/* x == &1 */
match x {
&y => /* y == 1 */
}
},
}
答案 1 :(得分:11)
ref
创建指向正在匹配的内存块的指针,在这种情况下,ref_to_x
直接指向存储point.x
的内存,它与在这种情况下写let ref_to_x = &point.x
。
模式非常重要,因为它允许人们深入到复杂的数据结构内部而不会干扰所有权层次结构。例如,如果有人val: &Option<String>
,则写
match *val {
Some(s) => println!("the string is {}", s),
None => println!("no string"
}
不合法,它会出现如下错误:
<anon>:3:11: 3:15 error: cannot move out of borrowed content
<anon>:3 match *val {
^~~~
<anon>:4:14: 4:15 note: attempting to move value to here
<anon>:4 Some(s) => {}
^
<anon>:4:14: 4:15 help: to prevent the move, use `ref s` or `ref mut s` to capture value by reference
<anon>:4 Some(s) => {}
^
从借用价值中取得所有权(移动)是不合法的,因为这可能会损害借用价值的东西(违反其不变量,导致数据意外消失等)。
因此,人们可以使用引用只是通过借用&
引用来指向内存。
这里有一点点微妙之处,因为(a)point
没有被借用,所以可以退出point
(消耗point
的所有权同样,意味着以后不能再使用它,除非重新初始化),(b)类型int
是Copy
,因此在按值使用时不会移动所有权。这就是使用myx
代替工作正常的原因。如果x
的类型是,例如,String
(不是Copy
}并且point
被借用,那么ref
将是必要的