问题描述
假设我有迭代器 let itt = some_string.chars().iter();
并且我有解析 json 的函数(或某种树搜索)是否可以在此函数的另一个递归调用之间来回移动迭代器?
更简单的例子:
fn f(it: Iter<i32>,level: i32) -> Iter<i32>{
let mut mit = it;
for num in mit {
if level > 2 {
println!("{} {}",level,num);
break
}
println!("{} {}",num);
mit = f(&mit,level+1);
}
mit
}
fn main() {
let tab = [1,2,3,4,5];
let mut itr = tab.iter();
f(itr,1);
}
预期输出:
1 1
2 2
3 3
2 4
3 5
顺便说一句,这个例子不起作用,让它起作用(或者说这不是要走的路)会解决这个问题,非常欢迎指出相关阅读材料
解决方法
由于两个原因,您编写的代码根本无法编译。首先,考虑表达式f(&mit,level + 1)
。这不会进行类型检查,因为 f
期望它的第一个参数是 Iter<i32>
类型,而是 &mit: &Iter<i32>
。所以你需要用 f(mit,level + 1)
替换它。我会解决这个问题。
第二个问题是 for
循环在 into_iter
上隐式调用 mit
,从而移动 mit
。 for
只是语法糖,所以我将手动对 for
循环进行脱糖以向您展示真正发生的事情:
fn f(it: Iter<i32>,level: i32) -> Iter<i32>{
let mut mit = it;
let mut iterator = mit.into_iter();
while let Some(num) = iterator.next() {
if level > 2 {
println!("{} {}",level,num);
break
}
println!("{} {}",num);
mit = f(mit,level+1);
}
mit
}
因为 Iter<i32>
没有实现 Copy
,这是一个使用后移动错误。我们将 mit
移到 into_iter
方法中,但我们再次尝试在 f(mit,level + 1)
的计算中使用它。
请注意,这实际上非常重要。通过以这种方式设置 for
循环,Rust 可以阻止您在迭代集合时以错误的方式改变集合,这可能导致意外结果(在某些情况下甚至可能导致未定义的行为)。
幸运的是,现在我们知道脱糖后的代码是什么样的,修复这个错误很容易。我们只是在脱糖代码中完全去掉变量 iterator
并得到以下内容:
fn f(it: Iter<i32>,level: i32) -> Iter<i32>{
let mut mit = it;
while let Some(num) = mit.next() {
if level > 2 {
println!("{} {}",level+1);
}
mit
}
这是因为对于类型 Iter<T>
,方法 into_iter()
只返回 Iter<T>
本身。
这将打印预期的输出。
1 1
2 2
3 3
2 4
3 5
根据需要。
编辑:
在您的特定情况下,我们可以按如下方式重写您的代码:
fn f(it: Iter<i32>,mut level: i32) {
for num in it {
println!("{} {}",num);
level = if level > 2 { level - 1 } else { level + 1 };
}
}
fn main() {
let tab = [1,2,3,4,5];
let itr = tab.iter();
f(itr,1);
}
完全消除了递归。