问题
It is quite clear to me that iterating over a vector shouldn't let the loop body mutate the vector arbitrarily. This prevents iterator invalidation, which is prone to bugs.
However, not all kinds of mutation lead to iterator invalidation. See the following example:
let mut my_vec: Vec<Vec<i32>> = vec![vec![1,2], vec![3,4], vec![5,6]];
for inner in my_vec.iter_mut() { // <- or .iter()
// ...
my_vec[some_index].push(inner[0]); // <-- ERROR
}
Such a mutation does not invalidate the iterator of my_vec
, however it is disallowed. It could invalidate any references to the specific elements in my_vec[some_index]
but we do not use any such references anyway.
I know that these questions are common, and I'm not asking for an explanation. I am looking for a way to refactor this so that I can get rid of this loop. In my actual code I have a huge loop body and I can't modularize it unless I express this bit nicely.
What I have thought of so far:
- Wrapping the vector with
Rc<RefCell<...>>
. I think this would still fail at runtime, since theRefCell
would be borrowed by the iterator and then will fail when the loop body tries to borrow it. - Using a temporary vector to accumulate the future pushes, and push them after the loop ends. This is okay, but needs more allocations than pushing them on the fly.
- Unsafe code, and messing with pointers.
- Anything listed in the Iterator documentation does not help. I checked out itertools and it looks like it wouldn't help either.
- Using a
while
loop and indexing instead of using an iterator making use of a reference to the outer vector. This is okay, but does not let me use iterators and adapters. I just want to get rid of this outer loop and usemy_vec.foreach(...)
.
Are there any idioms or any libraries which would let me do this nicely Unsafe functions would be okay as long as they don't expose pointers to me.
回答1:
You can wrap each of the inner vectors in a RefCell
.
use std::cell::RefCell;
fn main() {
let my_vec : Vec<RefCell<Vec<i32>>> = vec![
RefCell::new(vec![1,2]),
RefCell::new(vec![3,4]),
RefCell::new(vec![5,6])];
for inner in my_vec.iter() {
// ...
let value = inner.borrow()[0];
my_vec[some_index].borrow_mut().push(value);
}
}
Note that the value
binding here is important if you need to be able to push to the vector that inner
refers to. value
happens to be a type that doesn't contain references (it's i32
), so it doesn't keep the first borrow active (it ends by the end of the statement). Then, the next statement may borrow the same vector or another vector mutably and it'll work.
If we wrote my_vec[some_index].borrow_mut().push(inner.borrow()[0]);
instead, then both borrows would be active until the end of the statement. If both my_vec[some_index]
and inner
refer to the same RefCell<Vec<i32>>
, this will panic with RefCell<T> already mutably borrowed
.
回答2:
Without changing the type of my_vec
, you could simply use access by indexing and split_at_mut:
for index in 0..my_vec.len() {
let (first, second) = my_vec.split_at_mut(index);
first[some_index].push(second[0]);
}
Note: beware, the indices in second
are off by index
.
This is safe, relatively easy, and very flexible. It does not, however, work with iterator adaptors.
来源:https://stackoverflow.com/questions/35823029/how-to-mutate-another-item-in-a-vector-but-not-the-vector-itself-while-iterati