在迭代器内改变元素



我想在一个名为Test的结构体中作为成员包含的vector中的一些元素进行迭代。其思想是在每次迭代中独立地对Test进行突变,如果每个突变的Test上的某些外部逻辑成功,则表示成功。为简单起见,突变只是将向量元素更改为123u8。我遇到的问题是不能改变循环中的元素。我有两个解决方案,我认为会得到相同的答案:

#[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);
}

(游乐场)

输出:

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

预期输出:

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

如何在使用迭代器时更改self的成员?

正如您在文档中看到的那样,ìter采用&self,也就是说,无论您做什么,都不能修改self(您可以创建修改后的副本,但这不是您在这里要做的重点)。

相反,您可以使用iter_mut方法,它或多或少是相同的,但使用&mut self,也就是说,您可以修改它。

另一方面,您不想使用all,它用于检查属性是否在所有元素上为真(因此返回bool),相反,您想使用for_each,它将函数应用于所有元素。

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

(游乐场)


正如Stargateur在评论中提到的,你也可以使用for循环:

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

从Rust 1.50开始,有一个专门的方法来用一个值填充切片-[_]::fill:

self.vec.fill(number)

在这种情况下,fill似乎比for循环或for_each生成更少的代码:

(编译器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

最新更新