如何在 Rust 结构中同时更改两个字段?
How to alter two fields at once in a Rust struct?
考虑以下简单示例:
pub struct Bar {
curr: Vec<i32>,
prev: Vec<i32>,
}
pub fn main() {
let mut b = Bar { curr: vec![1, 2, 3], prev: vec![2, 3, 4] };
foo(&mut b);
}
pub fn foo(bar: &mut Bar) {
let next = vec![3, 4, 5];
bar.prev = bar.curr;
bar.curr = next;
}
使用Vec
无所谓;重点是 Bar
有两个字段没有实现 Copy
。这不编译:
error[E0507]: cannot move out of borrowed content
--> derp.rs:15:16
|
15 | bar.prev = bar.curr;
| ^^^ cannot move out of borrowed content
不难看出原因:通过移动 bar.curr
而不立即替换它,我们必须移动 bar
本身,我们不允许这样做,因为它只是可变借用的,没有实际拥有。
然而,这是一个非常常见的用例(在这种情况下——例如,保留函数的最后两个输出),我觉得必须有一个惯用的 Rust 用例。我意识到这可以通过使用单个元组 (curr, prev)
并立即分配它来解决,但是(假设函数 foo
是在结构 Bar
被使用很久之后编写的)重构可能非常令人沮丧。
一次分配两个值似乎不合法:代码 (bar.prev, bar.curr) = (bar.curr, next)
无法编译,因为左侧不是合法的左侧值。
编译以下代码有点意思:
pub struct Bar {
curr: Vec<i32>,
prev: Vec<i32>,
}
pub fn main() {
let b = Bar { curr: vec![1, 2, 3], prev: vec![2, 3, 4] };
foo(b);
}
pub fn foo(mut bar: Bar) -> Bar {
let next = vec![3, 4, 5];
bar.prev = bar.curr;
bar.curr = next;
bar
}
虽然 bar.prev = bar.curr
行似乎 需要 移动权限,但似乎 使用 它们,因为如果 bar
已移动,则下一行 bar.curr = next
不应编译。
此外,如果你把bar.curr = next
行out,它不再编译(bar
被移动后返回),所以看起来像编译器足够聪明,可以弄清楚如何解决这个问题(所有字段最终都被稳定分配),但不能对可变指针执行相同的任务。
所以我想 (a) 这是一个错误,(b) 这是一个 已知的 错误,并且 (c) 是否有解决方法,所以我仍然可以这样做可变指针?
您可以使用 std::mem::swap
:
pub fn foo(bar: &mut Bar) {
let next = vec![3, 4, 5];
std::mem::swap(&mut bar.prev, &mut bar.curr);
bar.curr = next;
}
使用std::mem::replace
or std::mem::swap
.
pub fn foo(bar: &mut Bar) {
use std::mem;
let next = vec![3, 4, 5];
bar.prev = mem::replace(&mut bar.curr, next);
}
It is somewhat interesting that the following code compiles [...]
这是因为您拥有该结构,所以编译器可以安全地将其分解。当结构被借用或在某种指针后面时,它不能这样做。关键问题是:如果你在修改的中途出现恐慌会发生什么(答案:调用堆栈中更高层的代码可能会观察到无效值,而 Rust 不会允许这种情况发生)。
这不是错误,这就是 Rust 的工作原理。
考虑以下简单示例:
pub struct Bar {
curr: Vec<i32>,
prev: Vec<i32>,
}
pub fn main() {
let mut b = Bar { curr: vec![1, 2, 3], prev: vec![2, 3, 4] };
foo(&mut b);
}
pub fn foo(bar: &mut Bar) {
let next = vec![3, 4, 5];
bar.prev = bar.curr;
bar.curr = next;
}
使用Vec
无所谓;重点是 Bar
有两个字段没有实现 Copy
。这不编译:
error[E0507]: cannot move out of borrowed content
--> derp.rs:15:16
|
15 | bar.prev = bar.curr;
| ^^^ cannot move out of borrowed content
不难看出原因:通过移动 bar.curr
而不立即替换它,我们必须移动 bar
本身,我们不允许这样做,因为它只是可变借用的,没有实际拥有。
然而,这是一个非常常见的用例(在这种情况下——例如,保留函数的最后两个输出),我觉得必须有一个惯用的 Rust 用例。我意识到这可以通过使用单个元组 (curr, prev)
并立即分配它来解决,但是(假设函数 foo
是在结构 Bar
被使用很久之后编写的)重构可能非常令人沮丧。
一次分配两个值似乎不合法:代码 (bar.prev, bar.curr) = (bar.curr, next)
无法编译,因为左侧不是合法的左侧值。
编译以下代码有点意思:
pub struct Bar {
curr: Vec<i32>,
prev: Vec<i32>,
}
pub fn main() {
let b = Bar { curr: vec![1, 2, 3], prev: vec![2, 3, 4] };
foo(b);
}
pub fn foo(mut bar: Bar) -> Bar {
let next = vec![3, 4, 5];
bar.prev = bar.curr;
bar.curr = next;
bar
}
虽然 bar.prev = bar.curr
行似乎 需要 移动权限,但似乎 使用 它们,因为如果 bar
已移动,则下一行 bar.curr = next
不应编译。
此外,如果你把bar.curr = next
行out,它不再编译(bar
被移动后返回),所以看起来像编译器足够聪明,可以弄清楚如何解决这个问题(所有字段最终都被稳定分配),但不能对可变指针执行相同的任务。
所以我想 (a) 这是一个错误,(b) 这是一个 已知的 错误,并且 (c) 是否有解决方法,所以我仍然可以这样做可变指针?
您可以使用 std::mem::swap
:
pub fn foo(bar: &mut Bar) {
let next = vec![3, 4, 5];
std::mem::swap(&mut bar.prev, &mut bar.curr);
bar.curr = next;
}
使用std::mem::replace
or std::mem::swap
.
pub fn foo(bar: &mut Bar) {
use std::mem;
let next = vec![3, 4, 5];
bar.prev = mem::replace(&mut bar.curr, next);
}
It is somewhat interesting that the following code compiles [...]
这是因为您拥有该结构,所以编译器可以安全地将其分解。当结构被借用或在某种指针后面时,它不能这样做。关键问题是:如果你在修改的中途出现恐慌会发生什么(答案:调用堆栈中更高层的代码可能会观察到无效值,而 Rust 不会允许这种情况发生)。
这不是错误,这就是 Rust 的工作原理。