【发布时间】:2015-09-20 12:06:29
【问题描述】:
我有一个有 2 个Vecs 的结构。我希望能够在修改另一个的同时迭代一个。这是一个示例程序:
use std::slice;
struct S {
a: Vec<i32>,
b: Vec<i32>
}
impl S {
fn a_iter<'a>(&'a self) -> slice::Iter<i32> {
self.a.iter()
}
fn a_push(&mut self, val: i32) {
self.a.push(val);
}
fn b_push(&mut self, val: i32) {
self.b.push(val);
}
}
fn main() {
let mut s = S { a: Vec::new(), b: Vec::new() };
s.a_push(1);
s.a_push(2);
s.a_push(3);
for a_val in s.a_iter() {
s.b_push(a_val*2);
}
}
但是有这个编译器错误:
$ rustc iterexample.rs
iterexample.rs:28:9: 28:10 error: cannot borrow `s` as mutable because it is also borrowed as immutable
iterexample.rs:28 s.b_push(a_val*2);
^
note: in expansion of for loop expansion
iterexample.rs:26:5: 29:6 note: expansion site
iterexample.rs:26:18: 26:19 note: previous borrow of `s` occurs here; the immutable borrow prevents subsequent moves or mutable borrows of `s` until the borrow ends
iterexample.rs:26 for a_val in s.a_iter() {
^
note: in expansion of for loop expansion
iterexample.rs:26:5: 29:6 note: expansion site
iterexample.rs:29:6: 29:6 note: previous borrow ends here
iterexample.rs:26 for a_val in s.a_iter() {
iterexample.rs:27 println!("Looking at {}", a_val);
iterexample.rs:28 s.b_push(a_val*2);
iterexample.rs:29 }
^
note: in expansion of for loop expansion
iterexample.rs:26:5: 29:6 note: expansion site
error: aborting due to previous error
我了解编译器在抱怨什么。我在 for 循环中借用了self,因为我还在循环它。
不过,从概念上讲,应该有办法做到这一点。我只是在修改s.b,而不是修改我正在循环的东西(s.a)。有没有办法编写我的程序来演示这种分离,并允许这种程序编译?
这是一个大型程序的简化示例,所以我需要保持一般结构相同(一个结构有一些东西,其中一个将被迭代,另一个将被更新)。
【问题讨论】:
-
我可能错了,不要相信我的话,这只是一个猜测,但我认为你不能这样做的概念原因是,如果推送到
s.b导致s在内存中重定位,那么对s.a的引用将失效。不过,您始终可以使用s.a.clone().iter()而不是s.a.iter()来解决此问题。 -
@Adrian 使用
s.a.iter和s.b.push实际上可以正常工作。我怀疑@Rory 希望在其非简化代码中保持字段私有(或者a_iter和b_push实际上可能更复杂,而不仅仅是Vec方法的简单包装)。 -
@Adrian 您的推理不成立的地方是,推送到
s.b不会重定位s,但会重定位s.b拥有的内部内存块,并由其特别指出私有ptr字段(参见Vec的实现)。 -
@fjh 你是对的。我想保密。我希望这些函数是对更复杂事物的“抽象”。
-
@Adrian 试过了,然后在
a_iter中得到一个关于“借用值不够长”的新错误。也得到与以前相同的错误“不能借用s作为可变的,因为它也被借用为不可变的”
标签: iterator rust borrow-checker