问题是 Rust 对于复制可变引用非常严格。这是一个问题,因为当你返回里面的迭代器时flat_map
,该迭代器必须具有对该点的可变(唯一)引用,但是flat_map
不够健壮,无法将迭代器返回给您,因此 Rust 无法证明在再次调用闭包时,最后一个迭代器仍然没有引用该点。一旦发电机稳定下来,正确地做到这一点将是微不足道的。在此期间,这仍然是可能的,但是MUCH比我想象的要难,你需要手动实现Iterator
特征。干得好:
游乐场链接
use std::iter::{ExactSizeIterator, FusedIterator};
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
struct Point {
x: i32,
y: i32,
}
impl Point {
fn new(x: i32, y: i32) -> Point {
Self { x, y }
}
}
#[derive(Debug)]
struct StepTracer<'a> {
point: &'a mut Point,
len: u8,
}
impl<'a> StepTracer<'a> {
fn new(point: &'a mut Point, len: u8) -> Self {
Self { point, len }
}
fn into_inner(self) -> &'a mut Point {
self.point
}
}
impl<'a> Iterator for StepTracer<'a> {
type Item = Point;
fn next(&mut self) -> Option<Self::Item> {
if self.len == 0 {
None
} else {
self.len -= 1;
self.point.x += 1;
Some(*self.point)
}
}
fn size_hint(&self) -> (usize, Option<usize>) {
(self.len as usize, Some(self.len as usize))
}
}
impl FusedIterator for StepTracer<'_> {}
impl ExactSizeIterator for StepTracer<'_> {}
// You may also want to consider implementing DoubleEndedIterator
// Additional traits: https://doc.rust-lang.org/std/iter/index.html#traits
enum MultiStepTracerState<'a> {
First(&'a mut Point),
Second(&'a mut Point),
Tracer(StepTracer<'a>),
Done,
}
/// Intention is that this produces points along a path defined by multiple
/// steps. Simplified.
struct MultiStepTracer<'a, I: Iterator<Item = u8>> {
steps: I,
state: MultiStepTracerState<'a>,
}
impl<'a, I: Iterator<Item = u8>> MultiStepTracer<'a, I> {
fn new(point: &'a mut Point, steps: I) -> Self {
Self {
steps,
state: MultiStepTracerState::First(point),
}
}
}
impl<I: Iterator<Item = u8>> Iterator for MultiStepTracer<'_, I> {
type Item = Point;
fn next(&mut self) -> Option<Self::Item> {
loop {
let mut temp_state = MultiStepTracerState::Done;
std::mem::swap(&mut self.state, &mut temp_state);
let point_ref = match temp_state {
MultiStepTracerState::First(point) => {
let result = *point;
self.state = MultiStepTracerState::Second(point);
return Some(result);
}
MultiStepTracerState::Second(point) => point,
MultiStepTracerState::Tracer(mut tracer) => {
if let Some(result) = tracer.next() {
self.state = MultiStepTracerState::Tracer(tracer);
return Some(result);
} else {
tracer.into_inner()
}
}
MultiStepTracerState::Done => {
return None;
}
};
if let Some(len) = self.steps.next() {
self.state = MultiStepTracerState::Tracer(StepTracer::new(point_ref, len));
} else {
self.state = MultiStepTracerState::Done;
return None;
}
}
}
}
impl<I: Iterator<Item = u8>> FusedIterator for MultiStepTracer<'_, I> {}
fn main() {
let mut point: Point = Point::new(0, 0);
let points: Vec<Point> = StepTracer::new(&mut point, 3).collect();
// Outputs: [Point { x: 1, y: 0 }, Point { x: 2, y: 0 }, Point { x: 3, y: 0 }]
println!("{:?}", points);
// Should trace the first from (0, 0) to (1, 0) and then trace the second step
// from (1, 0) to (2, 0) to (3, 0).
let points: Vec<Point> =
MultiStepTracer::new(&mut Point::new(0, 0), [1, 2].iter().copied()).collect();
println!("{:?}", points);
}