gpt4 book ai didi

rust - 正确拆分时,Rust不允许可变借用

转载 作者:行者123 更新时间:2023-12-03 11:28:59 34 4
gpt4 key购买 nike

struct Test {
a: i32,
b: i32,
}

fn other(x: &mut i32, _refs: &Vec<&i32>) {
*x += 1;
}

fn main() {
let mut xes: Vec<Test> = vec![Test { a: 3, b: 5 }];
let mut refs: Vec<&i32> = Vec::new();
for y in &xes {
refs.push(&y.a);
}
xes.iter_mut().for_each(|val| other(&mut val.b, &refs));
}

尽管 refs仅保存对 a中元素的 xes -member的引用,并且函数 other使用 b -member,但是rust产生以下错误:
error[E0502]: cannot borrow `xes` as mutable because it is also borrowed as immutable
--> /src/main.rs:16:5
|
13 | for y in &xes {
| ---- immutable borrow occurs here
...
16 | xes.iter_mut().for_each(|val| other(&mut val.b, &refs));
| ^^^ mutable borrow occurs here ---- immutable borrow later captured here by closure

Playground

闭包有问题吗?通常 splitting borrows应该允许这样做。我想念什么?

最佳答案

拆分借用仅在一个功能内起作用。不过,在这里,您要在a中借用字段main和在闭包中借用字段b(除了能够使用和借用外部作用域的变量外,这是一个独特的功能)。

从Rust 1.43.1开始,函数签名不能表示细粒度的借用;当引用(直接或间接)传递给函数时,它可以访问的所有。跨功能的借用检查基于功能签名;这部分是为了提高性能(在函数之间进行推理会付出更高的代价),部分是为了确保随着函数的发展(尤其是在库中)而实现的兼容性:构成函数有效参数的内容不应该依赖于函数的实现

据我了解,您的要求是您需要能够基于整个对象集的b字段的值来更新对象的a字段。

我看到两种方法可以解决此问题。首先,当我们捕获对b的共享引用时,我们可以同时捕获对a的所有可变引用。这是拆分借款的适当示例。这种方法的缺点是我们只需要分配两个Vec即可执行操作。

fn main() {
let mut xes: Vec<Test> = vec![Test { a: 3, b: 5 }];
let mut x_as: Vec<&i32> = Vec::new();
let mut x_bs: Vec<&mut i32> = Vec::new();
for x in &mut xes {
x_as.push(&x.a);
x_bs.push(&mut x.b);
}
x_bs.iter_mut().for_each(|b| other(b, &x_as));
}

这是使用迭代器构建两个 Vec的等效方法:
fn main() {
let mut xes: Vec<Test> = vec![Test { a: 3, b: 5 }];
let (x_as, mut x_bs): (Vec<_>, Vec<_>) =
xes.iter_mut().map(|x| (&x.a, &mut x.b)).unzip();
x_bs.iter_mut().for_each(|b| other(b, &x_as));
}

另一种方法是完全避免可变引用,而使用内部可变性。标准库具有 Cell,它对于 Copy类型(例如 i32RefCell)适用于所有类型,但确实在运行时借用了检查,从而增加了一些开销,还有 MutexRwLock,可以在多个线程中使用但执行锁定在运行时进行检查,以便最多一个线程可以随时访问内部值。

这是 Cell的示例。我们可以使用这种方法消除两个临时的 Vec,并且可以将整个对象集合传递给 other函数,而不仅仅是传递对 a字段的引用。
use std::cell::Cell;

struct Test {
a: i32,
b: Cell<i32>,
}

fn other(x: &Cell<i32>, refs: &[Test]) {
x.set(x.get() + 1);
}

fn main() {
let xes: Vec<Test> = vec![Test { a: 3, b: Cell::new(5) }];
xes.iter().for_each(|x| other(&x.b, &xes));
}

关于rust - 正确拆分时,Rust不允许可变借用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61699010/

34 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com