我有一个文件,需要逐行阅读并分成两个由“ =”分隔的句子。我正在尝试使用迭代器,但找不到在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等特征的特征?
答案 0 :(得分:4)
由于@Mateen commented,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
类型的迭代器(所有这些都是指向原始字符串的花哨的指针),从而消除了将原始字符串保留在范围内的依赖。
在您的情况下,几乎没有理由这样做,因为复制每个分片需要更多的处理能力和更多的内存,但是生锈可以为您提供这种控制。
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
上声明了它,即{{1 }}
您必须致电let words: Vec<String> = ...
,因为collect
除非您使用它,否则什么也不会做,但这超出了此答案的范围。