gpt4 book ai didi

rust - 在迭代另一个不可变字段时改变一个字段

转载 作者:行者123 更新时间:2023-11-29 07:46:02 25 4
gpt4 key购买 nike

给定以下程序:

struct Data {
pub items: Vec<&'static str>,
}

trait Generator {
fn append(&mut self, s: &str) {
self.output().push_str(s);
}

fn data(&self) -> &Data;

fn generate_items(&mut self) {
for item in self.data().items.iter() {
match *item {
"foo" => self.append("it was foo\n"),
_ => self.append("it was something else\n"),
}
}
}

fn output(&mut self) -> &mut String;
}

struct MyGenerator<'a> {
data: &'a Data,
output: String,
}

impl<'a> MyGenerator<'a> {
fn generate(mut self) -> String {
self.generate_items();

self.output
}
}

impl<'a> Generator for MyGenerator<'a> {
fn data(&self) -> &Data {
self.data
}

fn output(&mut self) -> &mut String {
&mut self.output
}
}

fn main() {
let data = Data {
items: vec!["foo", "bar", "baz"],
};

let generator = MyGenerator {
data: &data,
output: String::new(),
};

let output = generator.generate();

println!("{}", output);
}

编译时出现以下错误:

error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
--> src/main.rs:15:26
|
13 | for item in self.data().items.iter() {
| ---- - immutable borrow ends here
| |
| immutable borrow occurs here
14 | match *item {
15 | "foo" => self.append("it was foo\n"),
| ^^^^ mutable borrow occurs here

error[E0502]: cannot borrow `*self` as mutable because it is also borrowed as immutable
--> src/main.rs:16:22
|
13 | for item in self.data().items.iter() {
| ---- - immutable borrow ends here
| |
| immutable borrow occurs here
...
16 | _ => self.append("it was something else\n"),
| ^^^^ mutable borrow occurs here

构建代码的正确方法是什么,以便在迭代不可变字段 data 时可以写入可变字段 output?假设通过 Generator trait 的间接被用于与其他结构共享相似的逻辑,因此从 trait 的默认方法实现访问 MyStruct 的字段需要通过访问器来完成像这样的方法。

最佳答案

这是 Rust 中的一个常见问题;解决它的典型方法是替换舞蹈。这涉及使更多数据和方法使用可变引用:

struct Data {
pub items: Vec<&'static str>,
}

trait Generator {
fn append(&mut self, s: &str) {
self.output().push_str(s);
}

fn data(&mut self) -> &mut Data;

fn generate_items(&mut self) {
// Take the data. The borrow on self ends after this statement.
let data = std::mem::replace(self.data(), Data { items: vec![] });
// Iterate over the local version. Now append can borrow all it wants.
for item in data.items.iter() {
match *item {
"foo" => self.append("it was foo\n"),
_ => self.append("it was something else\n"),
}
}
// Put the data back where it belongs.
std::mem::replace(self.data(), data);
}
fn output(&mut self) -> &mut String;
}

struct MyGenerator<'a> {
data: &'a mut Data,
output: String,
}

impl<'a> MyGenerator<'a> {
fn generate(mut self) -> String {
self.generate_items();

self.output
}
}

impl<'a> Generator for MyGenerator<'a> {
fn data(&mut self) -> &mut Data {
self.data
}

fn output(&mut self) -> &mut String {
&mut self.output
}
}

fn main() {
let mut data = Data {
items: vec!["foo", "bar", "baz"],
};

let generator = MyGenerator {
data: &mut data,
output: String::new(),
};

let output = generator.generate();

println!("{}", output);
}

要意识到编译器有权提示。想象一下,如果调用 output() 的副作用是改变 data() 的返回值引用的内容,那么您在循环中使用的迭代器可能无效。你的特征函数有一个隐含的契约,即它们不会做那样的事情,但没有办法检查这一点。因此,您唯一能做的就是暂时完全控制数据,将其取出。

当然,这种模式会破坏展开安全;循环中的 panic 会使数据移出。

关于rust - 在迭代另一个不可变字段时改变一个字段,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35936995/

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