Reputation: 53
Let's say I have a vector, the values are from 1 to 10. I want that if you find 5 and 5 next to each other, remove them together with the next elements.
input
[1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10]
expected output
[1, 2, 3, 4]
This was my attempt. I'm finding index to remove, but borrowing rules are making me stuck.
let mut element = vec![1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10];
for (index, val) in element.iter().enumerate() {
if *val == 5 {
if let Some(next_val) = element.get(index + 1) {
if *next_val == 5 {
//element.drain(index..);
}
}
}
}
Upvotes: 2
Views: 2261
Reputation: 7100
You can't do what you want to do because you want to remove some elements from a vector while you are iterating it. And this is a big mistake. Note that removing any elements from a vector invalidates the iterators, hence you will access unexpected locations so rust doesn't allow UB
s
You can use something like the following
let mut elements = vec![1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10];
let mut first_repeated_five_index_op = None;
for index in 0..elements.len()-1{
if elements[index] == 5 && elements[index + 1] == 5{
first_repeated_five_index_op = Some(index);
break;
}
}
if let Some(first_repeated_five_index) = first_repeated_five_index_op{
elements.truncate(first_repeated_five_index);
}
println!("{:?}", elements);
See a Demo
Upvotes: 2
Reputation: 27905
Rust is saving you from iterator invalidation (a common source of bugs in other languages). This is an error that usually happens when you try to modify a data structure while concurrently iterating over it. You cannot move on to the (now-deleted) next element after calling element.drain(index..)
. So you need to add a break
after that point to avoid memory unsafety.
In this case just adding break;
is sufficient to make the code compile. However, for a more concise, linear solution, take full advantage of the iterators and methods provided by the standard library:
if let Some(index) = element.windows(2).position(|pair| pair[0] == pair[1]) {
element.truncate(index);
}
windows(2)
on a slice gives an iterator over subslices of length 2, and the position
call returns the index of the first element of that iterator for which the two elements of the slice are equal. (If no such pair exists, position
returns None
.)
I find that the position
closure becomes more obvious with the (currently unstable) array_windows
feature:
if let Some(index) = element.array_windows().position(|[x, y]| x == y) {
element.truncate(index);
}
Upvotes: 9