我想在一个名为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