Kevin
Kevin

Reputation: 3368

Mutating elements inside iterator

I would like to iterate over some elements inside a vector contained as a member in a struct called Test. The idea is to mutate Test independently in each iteration and signify success if some external logic on each mutated Test is successful. For simplicity, the mutation is just changing the vector element to 123u8. The problem I have is not being able to change the elements inside a loop. I have two solutions which I though would give the same answer:

#[derive(Debug)]
struct Test {
    vec: Vec<u8>
}

impl Test {
    fn working_solution(&mut self, number: u8) -> bool {
        self.vec[0] = number;
        self.vec[1] = number;
        self.vec[2] = number;
        
        true
    }
    
    fn non_working_solution(&mut self, number: u8) -> bool {
        self.vec.iter().all(|mut x| {
            x = &number; // mutation
            true // external logic
        })
    }
}


fn main() {

    let vec = vec![0u8,1u8,2u8];
    let mut test = Test { vec };
    
    println!("Original: {:?}", test);
    test.working_solution(123u8);
    println!("Altered: {:?}", test);
    
    let vec = vec![0u8,1u8,2u8];
    let mut test = Test { vec };
    println!("Original: {:?}", test);
    test.non_working_solution(123u8);
    println!("Altered: {:?}", test);
}

(playground)

Output:

Original: Test { vec: [0, 1, 2] }
Altered: Test { vec: [123, 123, 123] }
Original: Test { vec: [0, 1, 2] }
Altered: Test { vec: [0, 1, 2] }

Expected output:

Original: Test { vec: [0, 1, 2] }
Altered: Test { vec: [123, 123, 123] }
Original: Test { vec: [0, 1, 2] }
Altered: Test { vec: [123, 123, 123] }

How do I change a member of self when using an iterator?

Upvotes: 1

Views: 2890

Answers (2)

L. F.
L. F.

Reputation: 20639

Since Rust 1.50, there is a dedicated method for filling a slice with a value — [_]::fill:

self.vec.fill(number)

In this case, fill seems to generate less code than a for loop or for_each:

(Compiler Explorer)

pub fn f(slice: &mut [u8], number: u8) {
    slice.fill(number);
}

pub fn g(slice: &mut [u8], number: u8) {
    for x in slice {
        *x = number;
    }
}

pub fn h(slice: &mut [u8], number: u8) {
    slice
        .iter_mut()
        .for_each(|x| *x = number);
}
example::f:
  mov rax, rsi
  mov esi, edx
  mov rdx, rax
  jmp qword ptr [rip + memset@GOTPCREL]

example::g:
  test rsi, rsi
  je .LBB1_2
  push rax
  mov rax, rsi
  movzx esi, dl
  mov rdx, rax
  call qword ptr [rip + memset@GOTPCREL]
  add rsp, 8
.LBB1_2:
  ret

example::h:
  test rsi, rsi
  je .LBB2_1
  mov rax, rsi
  movzx esi, dl
  mov rdx, rax
  jmp qword ptr [rip + memset@GOTPCREL]
.LBB2_1:
  ret

Upvotes: 2

Bromind
Bromind

Reputation: 1138

As you can see in the documentation, ìter takes a &self, that is, whatever you do, you can not modify self (you can create a modified copy, but this is not the point of what you want to do here).

Instead, you can use the method iter_mut, which is more or less the same, but takes a &mut self, i.e., you can modify it.

An other side remark, you don't want to use all, which is used to check if a property is true on all elements (hence the bool returned), instead, you want to use for_each which applies a function to all elements.

fn non_working_solution(&mut self, number: u8) {
    self.vec.iter_mut().for_each(|x| {
        *x = number; // mutation
    })
}

(Playground)


As Stargateur mentioned in the comments, you can also use a for loop:

fn non_working_solution(&mut self, number: u8) {
    for x in self.vec.iter_mut() {
        *x = number
    }
}

Upvotes: 4

Related Questions