Struct 如何在结构字段上创建可变迭代器

Struct 如何在结构字段上创建可变迭代器,struct,rust,reference,iterator,mutable,Struct,Rust,Reference,Iterator,Mutable,因此,我正在使用Rust开发一个小的NES模拟器,我正试图用我的状态寄存器来表现自己。寄存器是一个结构,它包含一些包含bool的字段(标志),寄存器本身是CPU结构的一部分。现在,我想循环这些字段,并根据我执行的一些指令设置bool值。但是,我无法实现可变迭代器,我已经实现了into_iter()函数,并且能够遍历字段以获取/打印布尔值,但是如何在结构本身中对这些值进行变异?这可能吗 pub结构状态寄存器{ 结渣:bool, 零旗:布尔, 溢出标志:bool, } impl状态寄存器{ fn n

因此,我正在使用Rust开发一个小的NES模拟器,我正试图用我的状态寄存器来表现自己。寄存器是一个结构,它包含一些包含bool的字段(标志),寄存器本身是CPU结构的一部分。现在,我想循环这些字段,并根据我执行的一些指令设置bool值。但是,我无法实现可变迭代器,我已经实现了into_iter()函数,并且能够遍历字段以获取/打印布尔值,但是如何在结构本身中对这些值进行变异?这可能吗

pub结构状态寄存器{
结渣:bool,
零旗:布尔,
溢出标志:bool,
}
impl状态寄存器{
fn new()->Self{
状态寄存器{
结渣:是的,
ZeroFlag:false,
OverflowFlag:true,
}
}
}
恳求{
状态:&'a状态寄存器,
索引:usize,
}
恳求{
类型项=布尔;
fn下一步(&mut self)->选项{
让结果=匹配自索引{
0=>self.status.CarryFlag,
1=>self.status.ZeroFlag,
2=>self.status.OverflowFlag,
_=>返回无,
};
自指数+=1;
一些(结果)
}
}
发布结构CPU{
发布内存:[u8;0xffff],
发布状态:StatusRegister,
}
嵌入式CPU{
pub fn new()->CPU{
让内存=[0;0xFFFF];
中央处理器{
记忆,
状态:StatusRegister::new(),
}
}
fn执行(&mut self){
让mut移位器=0b1000_0000;
对于self.status.into_iter()中的状态{
//静音状态在这里!
println!(“{}”,状态);

shifter在可变引用上实现迭代器通常很难。如果迭代器两次返回对同一元素的引用,就会变得不可靠。这意味着,如果您想在纯粹安全的代码中编写一个迭代器,您必须以某种方式说服编译器每个元素只访问一次。这就排除了简单使用索引:y的可能性您可能总是忘记递增索引或将其设置在某个位置,编译器将无法对此进行推理


一种可能的方法是将几个
std::iter::once
s链接在一起(每个引用对应一个)

比如说,

impl StatusRegister {
    fn iter_mut(&mut self) -> std::vec::IntoIter<&mut bool> {
        vec![
            &mut self.CarryFlag,
            &mut self.ZeroFlag,
            &mut self.OverflowFlag,
        ]
        .into_iter()
    }
}
impl状态寄存器{
fn iter_mut(&mut self)->impl迭代器{
使用std::iter::一次;
一次(多个自结渣(&M)
.chain(一次(&mut self.ZeroFlag))
.chain(一次(&mut self.OverflowFlag))
}
}

正面:

  • 实现起来相当简单
  • 没有拨款
  • 没有外部依赖关系
缺点:

  • 迭代器的类型非常复杂:
    std::iter::Chain
因此,如果您不想使用
impl迭代器
,则必须在代码中包含该迭代器。这包括为
和mut StatusRegister
实现
IntoIterator
,因为您必须显式指示
IntoIter
类型


另一种方法是使用数组或
Vec
保存所有可变引用(具有正确的生存期),然后委托给迭代器实现以获取值。例如

impl StatusRegister {
    fn iter_mut(&mut self) -> std::vec::IntoIter<&mut bool> {
        vec![
            &mut self.CarryFlag,
            &mut self.ZeroFlag,
            &mut self.OverflowFlag,
        ]
        .into_iter()
    }
}
不安全性来自
next
方法,我们必须(本质上)在这里将
&mut&mut T T
类型的内容转换为
&mut T
,这通常是不安全的。但是,只要我们确保
next
不允许别名这些可变引用,我们就可以了。可能还有一些其他微妙的问题,所以我不能保证这是正确的。无论如何,MIRI没有找到任何pr这方面的问题

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)
    }
}
impl{
类型项=&'a mut bool;
//保持不变:索引为0、1、2或3
//每次通话,每次递增1次,上限为3次
//在两个不同的调用中,索引不应为0
//1和2的情况也一样。
fn下一步(&mut self)->选项{
让结果=不安全{
匹配自索引{
//安全性:因为这三个分支都是
//只执行一次,我们只发送一个可变引用
//自我状态的每一部分
//因为self.status对“a”有效
//每个部分借用对“a”也有效
0=>&mut*(&mut self.status.CarryFlag作为*mut_u2;),
1=>&mut*(&mut self.status.ZeroFlag为*mut)),
2=>&mut*(&mut-self.status.OverflowFlag为*mut_),
_=>返回无
}
};
//如果self.index不是0、1或2,那么我们已经返回了
//所以这会把我们增加到1,2或3。
自指数+=1;
一些(结果)
}
}

正面:

  • 没有拨款
  • 简单迭代器类型名
  • 没有外部依赖关系
缺点:

  • 实施起来很复杂。要成功使用
    不安全
    ,您需要非常熟悉什么是不允许的。这部分答案花了我最长的时间来确保我没有做错什么
  • 不安全会影响模块。在定义此迭代器的模块中,我可能会“安全地”通过弄乱
    status
    index
    statusregistermut
    字段而导致不健全。唯一允许封装的是在该模块之外,这些字段不可见

这是否回答了您的问题?副本没有说明的是,在您的情况下,您希望
&mut
&
有很大的区别,因为引用实现了复制,但可变引用没有。因此编译器需要在相同的
'a
生命周期内借用迭代器,以确保可变引用仅借用ce,但你不能这么做,因为锈没有GA