我只是想重新审视这个问题,因为现在有些细节有所不同。 (说实话,我自己就是不明白,所以我决定深入研究这些东西并记录我的发现。)
我们从这段代码开始:
use std::io::{stdin, BufRead};
fn main() {
for l in stdin().lock().lines() {
println!("{}", l.unwrap());
}
}
这是编译器必须说的:
t.rs:4:14: 4:21 error: borrowed value does not live long enough
t.rs:4 for l in stdin().lock().lines() {
^~~~~~~
t.rs:4:5: 6:6 note: reference must be valid for the destruction scope surrounding statement at 4:4...
t.rs:4 for l in stdin().lock().lines() {
t.rs:5 println!("{}", l.unwrap());
t.rs:6 }
t.rs:4:5: 6:6 note: ...but borrowed value is only valid for the statement at 4:4
t.rs:4 for l in stdin().lock().lines() {
t.rs:5 println!("{}", l.unwrap());
t.rs:6 }
t.rs:4:5: 6:6 help: consider using a `let` binding to increase its lifetime
让我们尝试一些更简单的方法:
fn main() {
let lock = stdin().lock();
}
这仍然不起作用,错误非常相似。这不起作用的事实告诉我们问题出在stdin() 调用上。
t.rs:4:16: 4:23 error: borrowed value does not live long enough
t.rs:4 let lock = stdin().lock();
^~~~~~~
t.rs:3:11: 5:2 note: reference must be valid for the block at 3:10...
t.rs:3 fn main() {
t.rs:4 let lock = stdin().lock();
t.rs:5 }
t.rs:4:5: 4:31 note: ...but borrowed value is only valid for the statement at 4:4
t.rs:4 let lock = stdin().lock();
^~~~~~~~~~~~~~~~~~~~~~~~~~
t.rs:4:5: 4:31 help: consider using a `let` binding to increase its lifetime
让我们看看类型。 stdin 返回Stdin,它有.lock 方法:
fn lock(&self) -> StdinLock
方法的返回类型是StdinLock,但是如果你看它的声明,你会发现它使用了一个生命周期参数:
pub struct StdinLock<'a> {
// some fields omitted
}
省略的字段是这个参数的原因,通过查阅sources我们得知里面有一个MutexGuard,并且生命周期限制是应用到守卫里面存储的值的类型。但这实际上根本不重要。重点是有这个生命周期参数,也就是说有lifetime elision参与,lock方法的声明其实是这样的:
fn lock<'a>(&'a self) -> StdinLock<'a> /* Self = Stdin */
所以。我们本地的lock 变量的类型为StdinLock<'a>。这个类型的'a 参数意味着StdinLock 内部有一些引用必须至少对'a 有效。另一方面,从 lock 是我们函数的局部变量这一事实我们知道它的范围是这个函数的主体,并且从它的类型是 StdinLock<'a> 的事实,编译器得出结论 'a 是函数体对应的范围。
此时我们意识到要使对.lock() 的调用有效,传递给它的self 参数必须在整个函数体中都处于活动状态,因为types 告诉我们.lock() 返回的值保留了对它的某些部分的引用。但它会在语句结束时立即被销毁,除非我们明确使用let 来帮助它延长寿命。
我们最终拥有:
use std::io::{stdin, BufRead};
fn main() {
let stdin = stdin();
for l in stdin.lock().lines() {
println!("{}", l.unwrap());
}
}
这恰好起作用。
就是这样,一如既往,一切都归结为所有权问题。从.lock() 返回的值不拥有它被调用的内容(在我们的例子中是stdin() 的结果),但它保留了对它的引用。这意味着必须有人负责保持stdin() 调用的结果并在某个时候销毁它,并且必须有人是你(和我),因为没有其他选择;)。
另一方面,.lines() 的类型是这样的:
fn lines(self) -> Lines<Self> /* Self = StdinLock */
如你所见,它消耗了self,所以我们不必显式绑定.lock()的结果,因为.lines()返回的值拥有锁的所有权,因此承担了保留它的责任只要它需要就活着,然后摧毁它。