gpt4 book ai didi

rust - 为什么在使用 `flat_map` 时需要收集到向量中?

转载 作者:行者123 更新时间:2023-11-29 07:50:04 29 4
gpt4 key购买 nike

我正在研究 Project Euler 96自学 Rust。我编写了这段代码来读取文件并将其转换为整数向量 ( Playground )。

let file = File::open(&args[1]).expect("Sudoku file not found");
let reader = BufReader::new(file);

let x = reader
.lines()
.map(|x| x.unwrap())
.filter(|x| !x.starts_with("Grid"))
.flat_map(|s| s.chars().collect::<Vec<_>>()) // <-- collect here!
.map(|x| x.to_digit(10).unwrap())
.collect::<Vec<_>>();

这一切都很好,但我很困惑为什么我必须在我的 flat_map 中收集到一个矢量(我假设创建不需要的矢量会立即被销毁是低效的)。如果我不收集,它就不会编译:

error[E0515]: cannot return value referencing function parameter `s`
--> src/main.rs:13:23
|
13 | .flat_map(|s| s.chars())
| -^^^^^^^^
| |
| returns a value referencing data owned by the current function
| `s` is borrowed here

例子来自 the docs显示几乎相同的代码,但不需要收集:

let words = ["alpha", "beta", "gamma"];

// chars() returns an iterator
let merged: String = words.iter()
.flat_map(|s| s.chars())
.collect();
assert_eq!(merged, "alphabetagamma");

那为什么我的代码不一样呢?

最佳答案

迭代器 reader.lines().map(|x| x.unwrap()) 迭代 String 项目,即按值。因此,在 .flat_map(|s| ...) 中,变量 s 的类型为 String(即拥有,而非借用)。换句话说:字符串现在是一个局部变量,存在于函数中。这是一个简单的规则,您不能返回对局部变量的引用(请参阅 this Q&A )。但这正是 s.chars() 所做的,即使它有点隐藏。

看一看at str::chars :

pub fn chars(&self) -> Chars<'_>

可以看出字符串是借用的。返回的 Chars 对象包含对原始字符串的引用。这就是我们不能从闭包中返回 s.chars() 的原因。

So why is it different in my code?

在文档的示例中,迭代器 words.iter() 实际上迭代类型为 &&'static str 的项目。调用 s.chars() 也将返回一个借用一些字符串的 Chars 对象,但该字符串的生命周期是 'static (永远存在),因此从该闭包返回 Chars 没有问题。

解决方案?

如果标准库有一个 OwnedChars 迭代器,它使用 String,就像 Chars 一样工作,一旦迭代器被丢弃。在这种情况下,可以调用 s.owned_chars(),因为返回的对象不引用本地 s,而是拥有它。但是:标准库中不存在这样的自有迭代器!

I'm assuming creating unneeded vectors which will be immediately destroyed is inefficient

是的,这在某种程度上是正确的。但是您可能错过了 reader.lines() 迭代器还会创建 String 类型的临时对象。那些或多或少也立即被摧毁了!因此,即使 flat_map 中没有 collect,您也会有一堆不必要的分配。请注意,有时这没关系。在这种情况下,我猜想与您必须实现的实际算法相比,输入解析速度非常快。那么...只是收集?在这种情况下可能没问题。

如果你想要高性能的输入解析,我认为你将无法避免标准循环,特别是为了避免不必要的 String 分配。 ( Playground )

let mut line = String::new();
let mut input = Vec::new();
loop {
line.clear(); // clear contents, but keep memory buffer

// TODO: handle IO error properly
let bytes_read = reader.read_line(&mut line).expect("IO error");
if bytes_read == 0 {
break;
}

if line.starts_with("Grid") {
continue;
}

// TODO: handle invalid input error
input.extend(line.trim().chars().map(|c| c.to_digit(10).unwrap()));
}

关于rust - 为什么在使用 `flat_map` 时需要收集到向量中?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57902478/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com