Как прокомментировал @ 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
ничего не делает, если вы его не используете, но это выходит за рамки этого ответа.