println!和from变量中的链迭代器之间的不同行为



为什么println!内联代码在这些示例中起作用,而不是可变变量?

我已经预定义了一些常量:

static ASCII_LOWERCASE: [char; 26] = [
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
];
static ASCII_UPPERCASE: [char; 26] = [
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
];
static ASCII_NUMERIC: [char; 10] = [
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
];
static ASCII_SYMBOLS: [char; 33] = [
'!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '-',
'_', '+', '=', '~', '`', '[', ']', '{', '}', '|', '\',
':', ';', '"', ''', '<', '>', ',', '.', '?', '/', ' ',
];

这工作并打印 95 个字符:

fn main() {
for x in 0..95 {
println!(
"{}",
ASCII_LOWERCASE
.into_iter()
.chain(ASCII_UPPERCASE.into_iter().chain(
ASCII_NUMERIC.into_iter().chain(
ASCII_SYMBOLS.into_iter(),
),
))
.nth(x)
.unwrap()
);
}
}

这只打印 13 个字符和恐慌:

fn main() {
let mut ascii = ASCII_LOWERCASE.into_iter().chain(
ASCII_UPPERCASE.into_iter().chain(
ASCII_NUMERIC.into_iter().chain(
ASCII_SYMBOLS.into_iter(),
),
),
);
for x in 0..95 {
println!("{}", ascii.nth(x).unwrap());
}
}

失败的输出:

a
c
f
j
o
u
B
J
S
2
$
`
,
thread 'main' panicked at 'called `Option::unwrap()` on a `None` value', /checkout/src/libcore/option.rs:335
note: Run with `RUST_BACKTRACE=1` for a backtrace.

失败的结果是跳过大多数字符,即使在同一迭代器上调用相同的方法也是如此。为什么要这样做?

为了让它按照我想要的方式工作,我必须这样做:

fn main() {
let ascii: Vec<&char> = ASCII_LOWERCASE
.into_iter()
.chain(ASCII_UPPERCASE.into_iter().chain(
ASCII_NUMERIC.into_iter().chain(
ASCII_SYMBOLS.into_iter(),
),
))
.collect();
for x in 0..95 {
println!("{}", ascii[x]);
}
}

为什么失败的版本行为不同?

从本质上讲,这就是迭代器和集合之间的区别。

迭代器将迭代一系列元素,永远不会生成相同的元素两次(尽管它可能产生相等的元素)。

集合是惰性的,只要按引用迭代,就可以多次迭代(使用into_iter通常会消耗集合,清空它)。


这意味着:

fn main() {
for x in 0..95 {
println!(
"{}",
ASCII_LOWERCASE
.into_iter()
.chain(ASCII_UPPERCASE.into_iter().chain(
ASCII_NUMERIC.into_iter().chain(
ASCII_SYMBOLS.into_iter(),
),
))
.nth(x)
.unwrap()
);
}
}

将,95次,创建一个迭代器链并到达x元素。这具有复杂性O(N2)。


另一方面:

fn main() {
let mut ascii = ASCII_LOWERCASE.into_iter().chain(
ASCII_UPPERCASE.into_iter().chain(
ASCII_NUMERIC.into_iter().chain(
ASCII_SYMBOLS.into_iter(),
),
),
);
for x in 0..95 {
println!("{}", ascii.nth(x).unwrap());
}
}

将创建一个迭代器一次,然后请求第 0 个元素,然后请求第一个剩余元素,然后请求第二个剩余元素。

线索在mut关键字中:每次调用nth时,迭代器都会发生突变(前进),并且您永远不会在循环迭代之间"倒带"。


最后:

fn main() {
let ascii: Vec<&char> = ASCII_LOWERCASE
.into_iter()
.chain(ASCII_UPPERCASE.into_iter().chain(
ASCII_NUMERIC.into_iter().chain(
ASCII_SYMBOLS.into_iter(),
),
))
.collect();
for x in 0..95 {
println!("{}", ascii[x]);
}
}

将创建一个集合一次,然后索引到该集合中。

请注意,您不需要在集合前面mut,因为它没有突变。


但是,正确的方法是停止使用索引。迭代器已经迭代了元素序列,因此您可以直接使用它:

fn main() {
let ascii = ASCII_LOWERCASE.iter().chain(
ASCII_UPPERCASE.iter().chain(
ASCII_NUMERIC.iter().chain(ASCII_SYMBOLS.iter())
)
);
for x in ascii {
println!("{}", x);
}
}

我想通了。 for 循环每次在工作示例中创建一个新集合,因此始终从一开始就引用nth集合。 迭代器通过next方法使用,因此失败的版本每次调用nth都会更改其参考点。

最新更新