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

文妮

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

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();
}
无伴奏合唱

通常,很难可变引用上实现迭代器如果迭代器两次对同一元素的引用返回两次,它将变得不合理。这意味着,如果您要用纯安全的代码编写代码,则必须以某种方式使编译器相信每个元素仅被访问一次。那只是使用索引就排除了:您总是会忘记增加索引或将其设置在某个位置,而编译器将无法对此进行推理。


一种可能的解决方法是将几个std::iter::onces链接在一起(每个要迭代的引用一个)。

例如,

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))
    }
}

(操场)

优点:

  • 实施起来相当简单。
  • 没有分配。
  • 没有外部依赖性。

缺点:

  • 迭代器的类型非常复杂:std::iter::Chain<std::iter::Chain<std::iter::Once<&mut bool>, std::iter::Once<&mut bool>>, std::iter::Once<&mut bool>>

因此,如果不想使用impl Iterator<Item = &mut bool>,则必须在代码中使用它。这包括实现IntoIteratorfor &mut StatusRegister,因为您必须明确指出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()
    }
}

(操场)

优点:

  • 该类型更易于管理std::vec::IntoIter<&mut bool>
  • 实施起来还是很简单的。
  • 没有外部依赖性。

缺点:

  • 每次iter_mut调用都需要分配

我还提到了使用数组。这样可以避免分配,但是事实证明,数组还没有实现对它们的值的迭代器,因此上面的代码用a[&mut bool; 3]代替Vec<&mut bool>不会起作用。但是,有一些板条箱可用于大小有限(例如arrayvecarray_vec)的固定长度阵列实现此功能

优点:

  • 没有分配。
  • 简单的迭代器类型。
  • 易于实现。

缺点:

  • 外部依赖性。

我要讨论的最后一种方法是使用unsafe由于与其他方法相比,它没有很多优点,因此一般而言,我不建议这样做。这主要是为了告诉你如何实现这一点。

像您的原始代码一样,我们将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,
}

不安全性来自于next方法,在方法中,我们将必须(本质上)将类型转换&mut &mut T&mut T,这通常是不安全的。但是,只要我们确保next不允许对这些可变引用使用别名,就可以了。可能还有其他一些细微的问题,所以我不能保证这是正确的。对于它的价值,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)
    }
}

(操场)

优点:

  • 没有分配。
  • 简单迭代器类型名称。
  • 没有外部依赖性。

缺点:

  • 实施复杂。要成功使用unsafe,您需要非常了解允许和不允许的内容。答案的这一部分花了我最长的时间来确保我没有做错什么。
  • 不安全会感染模块。在定义此迭代器的模块中,我可以通过与的statusindex字段弄乱来“安全”地导致不健全StatusRegisterIterMut唯一允许封装的是,在该模块之外,这些字段是不可见的。

本文收集自互联网,转载请注明来源。

如有侵权,请联系 [email protected] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

C ++如何在结构向量的一个字段上创建迭代器

如何在多个数组上创建可变循环迭代器

如何在结构中的stdin上存储迭代器?

如何使用返回可变引用的迭代器创建自己的数据结构?

在Rust中,如何创建可变的迭代器?

使用`flat_map`在结构切片的字段上创建迭代器

如何在堆栈上创建按值迭代器?

如何在Python中的字母上创建循环迭代器?

如何在Rust中定义一个包含可迭代项的结构上的迭代器?

如何在节点服务器上创建结构对象?

如何在RedwoodJS上创建字段解析器

如何在Rust中的锁定结构成员上返回迭代器?

如何在结构体中的数组块上实现迭代器?

如何组成可变的迭代器?

如果迭代器有生命周期,如何在可变迭代器上实现下一个方法?

如何在迭代器上循环?

如何创建一个带有迭代器的结构?

如何在 Rust 结构中保存迭代器

如何在@ManyToOne字段上创建@Index?

如何仅使某些结构字段可变?

如何在Flutter for mobile上创建可变形的网格UI?

如何创建迭代次数可变的for循环?

如何通过具有可变字段结构的JSON创建Java obj

如何在Vaadin的文本字段上创建实时验证器?

如何实现支持可变迭代器的容器?

如何在可变引用结构中为字段交换新值?

如何在Java中创建自定义迭代器?

如何在不使用可变变量的情况下在Scala中编程迭代器?

Javascript:如何在多维数组上实现迭代器?