[英]How do I create mutable iterator over struct fields
So I am working on a little NES emulator using Rust and I am trying to be fancy with my status register.因此,我正在使用 Rust 开发一个小型 NES 模拟器,并且我正在尝试使用我的状态寄存器。 The register is a struct that holds some fields (flags) that contain a bool, the register itself is part of a CPU struct.
寄存器是一个结构,它包含一些包含布尔值的字段(标志),寄存器本身是 CPU 结构的一部分。 Now, I want to loop through these fields and set the bool values based on some instruction I execute.
现在,我想遍历这些字段并根据我执行的一些指令设置布尔值。 However, am not able to implement a mutable iterator, I've implemented an into_iter() function and are able to iterate through the fields to get/print a bool value but how do I mutate these values within the struct itself?
但是,我无法实现可变迭代器,我已经实现了 into_iter() function 并且能够遍历字段以获取/打印 bool 值,但是如何在结构本身中改变这些值? Is this even possible?
这甚至可能吗?
pub struct StatusRegister {
CarryFlag: bool,
ZeroFlag: bool,
OverflowFlag: bool,
}
impl StatusRegister {
fn new() -> Self {
StatusRegister {
CarryFlag: true,
ZeroFlag: false,
OverflowFlag: true,
}
}
}
impl<'a> IntoIterator for &'a StatusRegister {
type Item = bool;
type IntoIter = StatusRegisterIterator<'a>;
fn into_iter(self) -> Self::IntoIter {
StatusRegisterIterator {
status: self,
index: 0,
}
}
}
pub struct StatusRegisterIterator<'a> {
status: &'a StatusRegister,
index: usize,
}
impl<'a> Iterator for StatusRegisterIterator<'a> {
type Item = bool;
fn next(&mut self) -> Option<bool> {
let result = match self.index {
0 => self.status.CarryFlag,
1 => self.status.ZeroFlag,
2 => self.status.OverflowFlag,
_ => return None,
};
self.index += 1;
Some(result)
}
}
pub struct CPU {
pub memory: [u8; 0xffff],
pub status: StatusRegister,
}
impl CPU {
pub fn new() -> CPU {
let memory = [0; 0xFFFF];
CPU {
memory,
status: StatusRegister::new(),
}
}
fn execute(&mut self) {
let mut shifter = 0b1000_0000;
for status in self.status.into_iter() {
//mute status here!
println!("{}", status);
shifter <<= 1;
}
}
}
fn main() {
let mut cpu = CPU::new();
cpu.execute();
}
Implementing an iterator over mutable references is hard in general.在可变引用上实现迭代器通常很难。 It becomes unsound if the iterator ever returns references to the same element twice.
如果迭代器曾经两次返回对同一元素的引用,它就会变得不健全。 That means that if you want to write one in purely safe code, you have to somehow convince the compiler that each element is only visited once.
这意味着如果你想用纯安全的代码编写一个,你必须以某种方式让编译器相信每个元素只被访问一次。 That rules out simply using an index: you could always forget to increment the index or set it somewhere and the compiler wouldn't be able to reason about it.
这排除了简单地使用索引:您总是可能忘记增加索引或将其设置在某处,编译器将无法推断它。
One possible way around is chaining together several std::iter::once
s (one for each reference you want to iterate over).一种可能的解决方法是将几个
std::iter::once
链接在一起(每个要迭代的引用一个)。
For example,例如,
impl StatusRegister {
fn iter_mut(&mut self) -> impl Iterator<Item = &mut bool> {
use std::iter::once;
once(&mut self.CarryFlag)
.chain(once(&mut self.ZeroFlag))
.chain(once(&mut self.OverflowFlag))
}
}
Upsides:优点:
Downsides:缺点:
std::iter::Chain<std::iter::Chain<std::iter::Once<&mut bool>, std::iter::Once<&mut bool>>, std::iter::Once<&mut bool>>
.std::iter::Chain<std::iter::Chain<std::iter::Once<&mut bool>, std::iter::Once<&mut bool>>, std::iter::Once<&mut bool>>
。 So you if don't want to use impl Iterator<Item = &mut bool>
, you'll have to have that in your code.因此,如果您不想使用
impl Iterator<Item = &mut bool>
,则必须在代码中使用它。 That includes implementing IntoIterator
for &mut StatusRegister
, since you'd have to explicitly indicate what the IntoIter
type is.这包括为
&mut StatusRegister
实现IntoIterator
,因为您必须明确指出IntoIter
类型是什么。
Another approach is using an array or Vec
to hold all the mutable references (with the correct lifetime) and then delegate to its iterator implementation to get the values.另一种方法是使用数组或
Vec
来保存所有可变引用(具有正确的生命周期),然后委托给它的迭代器实现以获取值。 For example,例如,
impl StatusRegister {
fn iter_mut(&mut self) -> std::vec::IntoIter<&mut bool> {
vec![
&mut self.CarryFlag,
&mut self.ZeroFlag,
&mut self.OverflowFlag,
]
.into_iter()
}
}
Upsides:优点:
std::vec::IntoIter<&mut bool>
.std::vec::IntoIter<&mut bool>
。 Downsides:缺点:
iter_mut
is called.iter_mut
时都需要分配。 I also mentioned using an array.我还提到了使用数组。 That would avoid the allocation, but it turns out that arrays don't yet implement an iterator over their values, so the above code with a
[&mut bool; 3]
这将避免分配,但事实证明 arrays尚未对其值实现迭代器,因此上面的代码带有
[&mut bool; 3]
[&mut bool; 3]
instead of a Vec<&mut bool>
won't work. [&mut bool; 3]
而不是Vec<&mut bool>
将不起作用。 However, there exist crates that implement this functionality for fixed-length arrays with limited size, eg arrayvec
(or array_vec
).但是,存在为大小有限的固定长度 arrays 实现此功能的板条箱,例如
arrayvec
(或array_vec
)。
Upsides:优点:
Downsides:缺点:
The last approach I'll talk about is using unsafe
.我要讨论的最后一种方法是使用
unsafe
。 Since this doesn't have many good upsides over the other approaches, I wouldn't recommend it in general.由于与其他方法相比,这没有太多好处,因此我一般不会推荐它。 This is mainly to show you how you could implement this.
这主要是向您展示如何实现这一点。
Like your original code, we'll implement Iterator
on our own struct.像您的原始代码一样,我们将在我们自己的结构上实现
Iterator
。
impl<'a> IntoIterator for &'a mut StatusRegister {
type IntoIter = StatusRegisterIterMut<'a>;
type Item = &'a mut bool;
fn into_iter(self) -> Self::IntoIter {
StatusRegisterIterMut {
status: self,
index: 0,
}
}
}
pub struct StatusRegisterIterMut<'a> {
status: &'a mut StatusRegister,
index: usize,
}
The unsafety comes from the next
method, where we'll have to (essentially) convert something of type &mut &mut T
to &mut T
, which is generally unsafe.不安全来自
next
方法,我们必须(本质上)将&mut &mut T
类型的东西转换为&mut T
,这通常是不安全的。 However, as long as we ensure that next
isn't allowed to alias these mutable references, we should be fine.但是,只要我们确保不允许
next
为这些可变引用设置别名,我们就可以了。 There may be some other subtle issues, so I won't guarantee that this is sound.可能还有其他一些微妙的问题,所以我不能保证这是正确的。 For what it's worth, MIRI doesn't find any problems with this.
对于它的价值,MIRI 没有发现任何问题。
impl<'a> Iterator for StatusRegisterIterMut<'a> {
type Item = &'a mut bool;
// Invariant to keep: index is 0, 1, 2 or 3
// Every call, this increments by one, capped at 3
// index should never be 0 on two different calls
// and similarly for 1 and 2.
fn next(&mut self) -> Option<Self::Item> {
let result = unsafe {
match self.index {
// Safety: Since each of these three branches are
// executed exactly once, we hand out no more than one mutable reference
// to each part of self.status
// Since self.status is valid for 'a
// Each partial borrow is also valid for 'a
0 => &mut *(&mut self.status.CarryFlag as *mut _),
1 => &mut *(&mut self.status.ZeroFlag as *mut _),
2 => &mut *(&mut self.status.OverflowFlag as *mut _),
_ => return None
}
};
// If self.index isn't 0, 1 or 2, we'll have already returned
// So this bumps us up to 1, 2 or 3.
self.index += 1;
Some(result)
}
}
Upsides:优点:
Downsides:缺点:
unsafe
, you need to be very familiar with what is and isn't allowed.unsafe
,您需要非常熟悉什么是允许的,什么是不允许的。 This part of the answer took me the longest by far to make sure I wasn't doing something wrong.status
or index
fields of StatusRegisterIterMut
.StatusRegisterIterMut
的status
或index
字段来“安全地”导致不健全。 The only thing allowing encapsulation is that outside of this module, those fields aren't visible.
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.