我试图在Rust中使用带有迭代器的模式并在某处掉落,显然很简单。
我想遍历一个容器并找到一个带有谓词[A](简单)的元素,但是然后使用另一个谓词向前看并得到该值[B]并使用[B]来变异[A] in某种方式。在这种情况下,[A]是可变的,[B]可以是不可变的;这对我没有任何影响,只对借阅检查员(正确)。
通过一个简单的场景帮助理解这一点,所以我添加了一个小片段,让民众看到问题/尝试的目标。我玩过itertools并进入for / while循环,虽然我想尽可能保持惯用。
愚蠢的示例方案
查找偶数,找到可被3整除的下一个数字并添加到初始数字。
#[allow(unused)]
fn is_div_3(num: &u8) -> bool {
num % 3 == 0
}
fn main() {
let mut data: Vec<u8> = (0..100).collect();
let count = data.iter_mut()
.map(|x| {
if *x % 2 == 0 {
// loop through numbers forward to next is_div_3,
// then x = x + that number
}
true
})
.count();
println!("data {:?}, count was {} ", data, count);
}
答案 0 :(得分:5)
可悲的是,我有点迟了,但现在就去了。
它并不完全漂亮,但它没有其他建议那么糟糕:
let mut data: Vec<u8> = (1..100).collect();
{
let mut mut_items = data.iter_mut();
while let Some(x) = mut_items.next() {
if *x % 2 == 0 {
let slice = mut_items.into_slice();
*x += *slice.iter().find(|&x| x % 3 == 0).unwrap();
mut_items = slice.iter_mut();
}
}
}
println!("{:?}", data);
给出
[1, 5, 3, 10, 5, 15, 7, 17, 9, 22, ...]
与Matthieu M.的解决方案一样。
关键是使用mut_items.into_slice()
来“重新借用”迭代器,有效地生成迭代器的本地(因此是安全的)克隆。
答案 1 :(得分:3)
警告:下面显示的迭代器是unsafe
,因为它允许一个多个别名获得一个可变元素;跳到修正版本的第二部分。 (如果返回类型包含不可变引用,那就没关系。)
如果你愿意编写自己的窗口迭代器,那么它就变得非常容易了。
首先,它的所有血腥细节都是迭代器:
use std::marker::PhantomData;
struct WindowIterMut<'a, T>
where T: 'a
{
begin: *mut T,
len: usize,
index: usize,
_marker: PhantomData<&'a mut [T]>,
}
impl<'a, T> WindowIterMut<'a, T>
where T: 'a
{
pub fn new(slice: &'a mut [T]) -> WindowIterMut<'a, T> {
WindowIterMut {
begin: slice.as_mut_ptr(),
len: slice.len(),
index: 0,
_marker: PhantomData,
}
}
}
impl<'a, T> Iterator for WindowIterMut<'a, T>
where T: 'a
{
type Item = (&'a mut [T], &'a mut [T]);
fn next(&mut self) -> Option<Self::Item> {
if self.index > self.len { return None; }
let slice: &'a mut [T] = unsafe {
std::slice::from_raw_parts_mut(self.begin, self.len)
};
let result = slice.split_at_mut(self.index);
self.index += 1;
Some(result)
}
}
在[1, 2, 3]
上调用它将返回(&[], &[1, 2, 3])
然后(&[1], &[2, 3])
,......直到(&[1, 2, 3], &[])
。简而言之,它迭代切片的所有潜在分区(不需要改组)。
可以安全使用:
fn main() {
let mut data: Vec<u8> = (1..100).collect();
for (head, tail) in WindowIterMut::new(&mut data) {
if let Some(element) = head.last_mut() {
if *element % 2 == 0 {
if let Some(n3) = tail.iter().filter(|i| *i % 3 == 0).next() {
*element += *n3;
}
}
}
}
println!("{:?}", data);
}
不幸的是它也可以用作:
fn main() {
let mut data: Vec<u8> = (1..100).collect();
let mut it = WindowIterMut::new(&mut data);
let first_0 = { it.next(); &mut it.next().unwrap().0[0] };
let second_0 = &mut it.next().unwrap().0[0];
println!("{:?} {:?}", first_0 as *const _, second_0 as *const _);
}
当运行print:0x7f73a8435000 0x7f73a8435000
时,show-casing表示两个可变引用都是同一个元素。
由于我们无法摆脱锯齿,我们需要摆脱可变性;或者至少遵守内部可变性(Cell
,因为u8
是Copy
)。
幸运的是,Cell
没有运行时成本,但它在人体工程学方面确实有些成本(所有.get()
和.set()
)。
我借此机会使迭代器稍微更通用,并重命名它,因为Window
已经是另一个概念的已使用名称。
struct FingerIter<'a, T>
where T: 'a
{
begin: *const T,
len: usize,
index: usize,
_marker: PhantomData<&'a [T]>,
}
impl<'a, T> FingerIter<'a, T>
where T: 'a
{
pub fn new(slice: &'a [T]) -> FingerIter<'a, T> {
FingerIter {
begin: slice.as_ptr(),
len: slice.len(),
index: 0,
_marker: PhantomData,
}
}
}
impl<'a, T> Iterator for FingerIter<'a, T>
where T: 'a
{
type Item = (&'a [T], &'a T, &'a [T]);
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.len { return None; }
let slice: &'a [T] = unsafe {
std::slice::from_raw_parts(self.begin, self.len)
};
self.index += 1;
let result = slice.split_at(self.index);
Some((&result.0[0..self.index-1], result.0.last().unwrap(), result.1))
}
}
我们将它用作建筑砖:
fn main() {
let data: Vec<Cell<u8>> = (1..100).map(|i| Cell::new(i)).collect();
for (_, element, tail) in FingerIter::new(&data) {
if element.get() % 2 == 0 {
if let Some(n3) = tail.iter().filter(|i| i.get() % 3 == 0).next() {
element.set(element.get() + n3.get());
}
}
}
let data: Vec<u8> = data.iter().map(|cell| cell.get()).collect();
println!("{:?}", data);
}
On the playpen打印:[1, 5, 3, 10, 5, 15, 7, 17, 9, 22, ...]
,这似乎是正确的。