Abd*_*awy 2 compiler-errors compilation function method-chaining rust
当我遇到此错误时,我正在解析文件中的一些字符串输入。通常,如果将一系列方法链接在一行上或将它们分成多个操作,则不会产生任何影响。然而,当方法链在一行中时,它不会编译。
当像这样分割成多个语句时,我不会收到错误(链接到游乐场)
let input = std::fs::read_to_string("tst_input.txt").expect("Failed to read input");
let input = input
.lines()
.map(|l| {
let mut iter = l.split(" | ");
(
iter.next()
.unwrap()
.split_whitespace()
.collect::<Vec<&str>>(),
iter.next()
.unwrap()
.split_whitespace()
.collect::<Vec<&str>>(),
)
})
.collect::<Vec<_>>();
Run Code Online (Sandbox Code Playgroud)
当它出现在像这样的单个语句中时,我会遇到终身错误(链接到游乐场)
let input = std::fs::read_to_string("tst_input.txt")
.expect("Failed to read input")
.lines()
.map(|l| {
let mut iter = l.split(" | ");
(
iter.next()
.unwrap()
.split_whitespace()
.collect::<Vec<&str>>(),
iter.next()
.unwrap()
.split_whitespace()
.collect::<Vec<&str>>(),
)
})
.collect::<Vec<_>>()
Run Code Online (Sandbox Code Playgroud)
error[E0716]: temporary value dropped while borrowed
--> src/main.rs:2:17
|
2 | let input = std::fs::read_to_string("tst_input.txt")
| _________________^
3 | | .expect("Failed to read input")
| |_______________________________________^ creates a temporary which is freed while still in use
...
18 | .collect::<Vec<_>>();
| - temporary value is freed at the end of this statement
19 | println!("{:?}", input);
| ----- borrow later used here
|
= note: consider using a `let` binding to create a longer lived value
Run Code Online (Sandbox Code Playgroud)
这两种情况应该实际上相同吗?为什么编译器以不同的方式对待它们?这可能是编译器错误吗?
这两种情况并不相同,因为存储的信息不同。
在 Rust 中,变量具有语义意义:它们充当存储信息的地方,更重要的是,它们定义了此信息何时被销毁 - 这是由DropTrait处理的。默认情况下,drop每个超出范围的变量都会调用方法;这可以被mem::forget和其他一些函数覆盖,例如Box::into_raw,但这些都是相当小众的情况。
在第一种情况下,正在读取的数据存储到input类型的变量中String。该类型封装Vec<u8>了,它实现了Drop,因此当input超出范围时该数据将被释放。然后,第二个input变量是类型Vec<(Vec<&str>, Vec<&str>)>- 您可以看到它包含一个引用,因此它是从第一个变量借用的input,因此它的生存时间必须不再是源字符串。在这里,这是满足的 - 当然,只要您不尝试在堆栈中返回该值,在这种情况下源字符串将被删除,并且引用将悬空。
然而,在单行版本中,字符串不存储在任何地方 - 它是一个临时的,在语句末尾被销毁。这就是为什么你不被允许保留任何对它的引用。但是,您可以通过插入额外的映射操作来制作分割数据的自有版本:
let _: Vec<(Vec<String>, Vec<String>)> = std::fs::read_to_string("tst_input.txt")
.expect("Failed to read input")
// This iterator borrows from the temporary...
.lines()
.map(|l| {
// ...this iterator reborrows that borrow...
let mut iter = l.split(" | ");
(
iter.next()
.unwrap()
.split_whitespace()
// ...and this operation clones the source data,
// so they are copied to the new owned location,
// and not referenced anymore, so can be freely dropped
.map(str::to_owned)
.collect::<Vec<_>>(),
iter.next()
.unwrap()
.split_whitespace()
.map(str::to_owned)
.collect::<Vec<_>>(),
)
})
.collect::<Vec<_>>();
Run Code Online (Sandbox Code Playgroud)