Rust impl Iterator for Range<T> on custom type
Rust impl Iterator for Range<T> on custom type
我有一个点结构,像这样:
#[derive(Clone, Copy, Debug)]
struct Point {
x: i32,
y: i32,
}
我想像这样在点之间迭代:
let start = Point { x: 0, y: 0 };
let end = Point { x: 3, y: 3 };
for p in start..end {
// p should be
// (0, 0)
// (1, 0)
// (2, 0)
// (0, 1)
// (1, 1)
// (2, 1)
// (0, 2)
// etc
}
但是,我似乎无法实现这一点。 start..end
产生一个 Range<Pos>
,这是标准库中的一个类型。所以我无法自定义 impl Iterator for Range<Pos>
,因为这些类型中的 none 在当前板条箱中。
我也不能 impl Step for Point
,因为迭代器必须知道范围的开始和结束。 Step
不会给你这个信息。
最后,我无法为 Iterator
编写包装器,因为 impl MyIterator for Range<Point>
会失败并出现错误,指出 Point
未实现 Step
。
有什么办法可以解决吗?我目前的解决方案是在Point
上定义一个to
函数,这样你就可以这样写:
for p in start.to(end) {
// works as expected
}
to
函数生成一个自定义迭代器,它可以满足我的需求。但是,我仍然想使用 ..
语法。
编辑:AFAIK,this 是它工作所需要的。根据这些评论,这看起来不可能。
正如您发现的那样,您不能直接执行此操作。但是,我不会太在意语法,因为 point..point
不一定会使代码更具可读性。事实上,它隐藏了有关排序的信息,并使以后添加按列顺序而不是行顺序的迭代器变得更加困难。
传达顺序的设计,同时仍然使用范围语法可以是:
fn row_major(range: Range<Point>) -> impl Iterator<Item = Point> {
(range.start.y..range.end.y)
.flat_map(move |y| (range.start.x..range.end.x).map(move |x| Point { x, y }))
}
fn main() {
let start = Point { x: 0, y: 0 };
let end = Point { x: 2, y: 2 };
let result: Vec<_> = row_major(start..end).collect();
assert_eq!(
result,
vec![
Point { x: 0, y: 0 },
Point { x: 1, y: 0 },
Point { x: 0, y: 1 },
Point { x: 1, y: 1 },
]
)
}
row_major
可能不是最佳名称,具体取决于您的应用程序。其他选项如 horizontal_iter
、hrange
或 iter_rows
。给它一个这样的名字可以让 reader 清楚地知道期望的顺序,并且还可以在以后添加函数的对称列主(垂直)版本。
您还可以扩展它以支持 ..end
和 start..=end
变体以获得更多表现力,同时排除此处没有意义的 start..
。
我有一个点结构,像这样:
#[derive(Clone, Copy, Debug)]
struct Point {
x: i32,
y: i32,
}
我想像这样在点之间迭代:
let start = Point { x: 0, y: 0 };
let end = Point { x: 3, y: 3 };
for p in start..end {
// p should be
// (0, 0)
// (1, 0)
// (2, 0)
// (0, 1)
// (1, 1)
// (2, 1)
// (0, 2)
// etc
}
但是,我似乎无法实现这一点。 start..end
产生一个 Range<Pos>
,这是标准库中的一个类型。所以我无法自定义 impl Iterator for Range<Pos>
,因为这些类型中的 none 在当前板条箱中。
我也不能 impl Step for Point
,因为迭代器必须知道范围的开始和结束。 Step
不会给你这个信息。
最后,我无法为 Iterator
编写包装器,因为 impl MyIterator for Range<Point>
会失败并出现错误,指出 Point
未实现 Step
。
有什么办法可以解决吗?我目前的解决方案是在Point
上定义一个to
函数,这样你就可以这样写:
for p in start.to(end) {
// works as expected
}
to
函数生成一个自定义迭代器,它可以满足我的需求。但是,我仍然想使用 ..
语法。
编辑:AFAIK,this 是它工作所需要的。根据这些评论,这看起来不可能。
正如您发现的那样,您不能直接执行此操作。但是,我不会太在意语法,因为 point..point
不一定会使代码更具可读性。事实上,它隐藏了有关排序的信息,并使以后添加按列顺序而不是行顺序的迭代器变得更加困难。
传达顺序的设计,同时仍然使用范围语法可以是:
fn row_major(range: Range<Point>) -> impl Iterator<Item = Point> {
(range.start.y..range.end.y)
.flat_map(move |y| (range.start.x..range.end.x).map(move |x| Point { x, y }))
}
fn main() {
let start = Point { x: 0, y: 0 };
let end = Point { x: 2, y: 2 };
let result: Vec<_> = row_major(start..end).collect();
assert_eq!(
result,
vec![
Point { x: 0, y: 0 },
Point { x: 1, y: 0 },
Point { x: 0, y: 1 },
Point { x: 1, y: 1 },
]
)
}
row_major
可能不是最佳名称,具体取决于您的应用程序。其他选项如 horizontal_iter
、hrange
或 iter_rows
。给它一个这样的名字可以让 reader 清楚地知道期望的顺序,并且还可以在以后添加函数的对称列主(垂直)版本。
您还可以扩展它以支持 ..end
和 start..=end
变体以获得更多表现力,同时排除此处没有意义的 start..
。