Как мне создать итератор строк из файла, который был разбит на части? - PullRequest
0 голосов
/ 27 декабря 2018

У меня есть файл, который мне нужно прочитать построчно и разбить на два предложения, разделенных знаком «=».Я пытаюсь использовать итераторы, но не могу найти, как правильно его использовать в split.Документация говорит, что std::str::Split реализует черту, но я все еще не знаю, как ее использовать.

use std::{
    fs::File,
    io::{prelude::*, BufReader},
};

fn example(path: &str) {
    for line in BufReader::new(File::open(path).expect("Failed at opening file.")).lines() {
        let words = line.unwrap().split("="); //need to make this an iterable
    }
}

Как я могу использовать черту, которая, как я знаю, уже реализована в нечто вроде split?

1 Ответ

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

Как прокомментировал @ Mateen , split уже возвращает итерацию.Чтобы исправить проблемы времени жизни, сохраните значение, возвращаемое unwrap(), в переменную перед вызовом split.

Я попытаюсь объяснить здесь проблему времени жизни.

Сначала это действительно помогает взглянуть на сигнатуры функций.

pub fn unwrap(self) -> T
pub fn split<'a, P: Pattern<'a>>(&'a self, pat: P) -> Split<'a, P>

unwrap довольно прост, он берет на себя ответственность и возвращает внутреннее значение.

split выглядит страшно, но это не так уж сложно, 'a - это просто имя для времени жизни, и оно просто указывает, как долго можно использовать возвращаемое значение.В этом случае это означает, что оба входных аргумента должны жить как минимум столько же, сколько и возвращаемое значение.

//                   Takes by reference, no ownership change
//                               v
pub fn split<'a, P: Pattern<'a>>(&'a self, pat: P) -> Split<'a, P>
//           ^              ^      ^                         ^
//           |              |--|---|                         |
// This just declares a name.  |                             |
//                             |                             |
//           Both of these values must last longer than -----|

Это потому, что split не копирует ни одну из строк, он просто указывает наположение на исходной строке, где происходит разделение.Если исходная строка по какой-либо причине была отброшена, Split не будет указывать на недопустимые данные.

Время жизни переменной (если владение не передано другому объекту) длится до тех пор, пока оно не выйдет из области видимости, этолибо в конце }, если он назван (например, с let), либо в конце строки / ;

Вот почему в вашем коде существует проблема времени жизни:

for line in std::io::BufReader::new(std::fs::File::open(path).expect("Failed at opening file.")).lines() {
    let words = line
        .unwrap() // <--- Unwrap consumes `line`, `line` can not be used after calling unwrap(),
        .split("=") // Passed unwrap()'s output to split as a reference
        ; //<-- end of line, unwrap()'s output is dropped due to it not being saved to a variable, the result of split now points to nothing, so the compiler complains.
}

Решения

Сохранение возвращаемого значения unwrap()

for line in std::io::BufReader::new(std::fs::File::open("abc").expect("Failed at opening file.")).lines() {
    let words = line.unwrap();
    let words_split = words.split("=");
} // <--- `word`'s lifetime ends here, but there is no lifetime issues since `words_split` also ends here.

Вы можете переименовать words_split в words, чтобы скрыть исходную переменную, чтобы не загромождать имена переменныхесли вы хотите, это также не вызывает проблем, поскольку теневые переменные удаляются не сразу, а в конце своей первоначальной области видимости.

Или

Вместо того, чтобы иметь итератор типа str, каждый из которых является просто причудливым указателем на исходную строку, вы можете скопировать каждый фрагмент в егособственной строки, устраняя зависимость от сохранения исходной строки в области видимости.

В вашем случае почти наверняка нет причин делать это, поскольку копирование каждого фрагмента требует больше вычислительной мощности и больше памяти, но ржавчина дает вам этоcontrol.

let words = line
    .unwrap()
    .split("=")
    .map(|piece|
        piece.to_owned() // <--- This copies all the characters in the str into it's own String.
    ).collect::<Vec<String>>()
    ; // <--- unwrap()'s output dropped here, but it doesn't matter since the pieces no longer points to the original line string.

let words_iterator = words.iter();

collect выдает ошибку cannot infer type, потому что вы не указали то, что хотите собирать, либо используете синтаксис TurboFish выше, либо укажите words, т.е.1057 *

Вы должны позвонить collect, потому что map ничего не делает, если вы его не используете, но это выходит за рамки этого ответа.

...