Создание итератора слов из линейного итератора - PullRequest
0 голосов
/ 03 декабря 2018

У меня есть строковый итератор lines, который я получаю из стандартного ввода с

use std::io::{self, BufRead};

let mut stdin = io::stdin();
let lines = stdin.lock().lines().map(|l| l.unwrap());

Итератор lines выдает значения типа String, а не &str.Я хочу создать итератор, который перебирает входные слова вместо строк.Кажется, что это должно быть выполнимо, но моя наивная попытка не работает:

let words = lines.flat_map(|l| l.split_whitespace());

Компилятор говорит мне, что l отбрасывается, пока он заимствован, что имеет смысл:

error[E0597]: `l` does not live long enough
 --> src/lib.rs:6:36
  |
6 |     let words = lines.flat_map(|l| l.split_whitespace());
  |                                    ^                  - `l` dropped here while still borrowed
  |                                    |
  |                                    borrowed value does not live long enough
7 | }
  | - borrowed value needs to live until here

Есть ли какой-то другой чистый способ, который выполняет это?

1 Ответ

0 голосов
/ 04 декабря 2018

В вашем примере кода, 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 и возвращает итератор, которому принадлежит строка.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...