В вашем примере кода, lines
- это итератор строк, считанных из программы чтения, которую вы получили из stdin
.Как вы говорите, он возвращает String
экземпляров, но вы их нигде не храните.
std::string::String::split_whitespace
определяется следующим образом:
pub fn split_whitespace(&self) -> SplitWhitespace
Итак, он принимает ссылку настрока - она не потребляет строку.Он возвращает итератор, который выдает строковые фрагменты &str
- которые ссылаются на части строки, но не владеют ею.
Фактически, как только с ним завершено закрытие, которое вы передали flat_map
, никто не владеет им, поэтому оно сбрасывается.Это приведет к тому, что &str
будет уступать words
, что приведет к ошибке.
Одно из решений состоит в том, чтобы собрать линии в вектор, например:
let lines: Vec<String> = stdin.lock().lines().map(|l| l.unwrap()).collect();
let words = lines.iter().flat_map(|l| l.split_whitespace());
String
экземпляры хранятся в Vec<String>
, который может жить так, чтобы у &str
, полученного words
, было что-то, на что можно сослаться.
Если было много строк, и вы не хотеличтобы сохранить их все в памяти, вы можете предпочесть делать это по очереди:
let lines = stdin.lock().lines().map(|l| l.unwrap());
let words = lines.flat_map(|l| {
l.split_whitespace()
.map(|s| s.to_owned())
.collect::<Vec<String>>()
.into_iter()
});
Здесь слова каждой строки собраны в Vec
, по одной строке.Компромисс заключается в меньшем общем потреблении памяти по сравнению с издержками на создание Vec<String>
для каждой строки и копированием в нее каждого слова.
Возможно, вы надеялись на реализацию с нулевым копированием, которая потребляла быStrings
, который lines
производит.Я думаю, что это можно было бы создать, создав функцию split_whitespace()
, которая принимает владение String
и возвращает итератор, которому принадлежит строка.