How do you idiomatically iterate over a field in self
while using other fields of self?
Example:
Some context
#[derive(Clone, Copy)]
struct Foo {
foo: i32
}
struct FooContainer {
items: Vec<Foo>,
incremented: usize
}
impl FooContainer {
// here the actual example function
fn increment_even_items_and_delete_the_ones_over_7(&mut self) {
for item in &mut self.items {
if item.foo % 2 == 0 {
item.foo += 1;
self.record_increment(); // cannot borrow because already borrowed 3 lines earlier (&mut self items)
}
}
self.items.retain(|item| {
item.foo <= 7
});
}
fn new() -> Self {
FooContainer {
items: (0..10).map(|i| Foo { foo: i }).collect(),
incremented: 0
}
}
fn record_increment(&mut self) {
self.incremented += 1;
}
}
One solution would be using a counter for the iteration:
fn increment_even_items_and_delete_the_ones_over_7(&mut self) {
for i in 0..self.items.len() {
let item = &mut self.items[i];
if item.foo % 2 == 0 {
item.foo += 1;
self.record_increment();
}
}
self.items.retain(|item| {
item.foo <= 7
});
}
}
Is there a more idiomatic way than the counter iteration?
tuffy
May 29, 2020, 4:13pm
2
One idea is to simply forego the record_increment()
call altogether, like:
fn increment_even_items_and_delete_the_ones_over_7(&mut self) {
self.incremented += self
.items
.iter_mut()
.map(|item| {
if item.foo % 2 == 0 {
item.foo += 1;
1
} else {
0
}
})
.sum::<usize>();
self.items.retain(|item| item.foo <= 7);
}
H2CO3
May 29, 2020, 4:22pm
3
Another is to make the methods borrow only the subset of fields that they actually need, and not the entirety of self
.
Actually the question even a bit more general.
What is the idomatic way of implementing this pattern
for item in &self.items {
self.do_something_with_item_that_mutates_self(*item);
}
I see the following solutions:
1. Clone self.items
(overhead, violating zero cost demand)
for item in self.items.clone() {
self.do_something_with_item_that_mutates_self(item);
}
2. C-style loop
for i in 0..self.items.len() {
self.do_something_with_item_that_mutates_self(self.items[i]);
}
3. Ditch the method do_something_with_item_that_mutetes_self()
Instead write everything directly into the for
loop. → Duplicated code if I need the method elsewhere.
What's the "That's the way to do it" way?
There is no idiomatic way to do this, because you're telling the compiler that you want to both iterate self.items
through a shared reference and take an exclusive reference to self
. There is a clear conflict of interest here.
The idiomatic way to rearrange your code so the compiler understands your intent is to only borrow disjoint fields. There has been some discussion on several options in the past. This thread is always a good reference: Blog post series: After NLL -- what's next for borrowing and lifetimes?
2 Likes
system
Closed
August 28, 2020, 12:57am
6
This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.