-3

我写这个是为了简单的输入解析:

use std::io;

fn main() {
    let mut line = String::new();

    io::stdin().read_line(&mut line)
        .expect("Cannot read line.");

    let parts = line.split_whitespace();

    for p in parts {
        println!("{}", p);
    }

    line.clear();
    io::stdin().read_line(&mut line)
        .expect("Cannot read line.");

}

上面的代码创建一个String对象,读入一行,用空格分割它并打印输出。然后它尝试使用相同的String对象做同样的事情。编译时出现错误:

  --> src/main.rs:15:5
   |
9  |     let parts = line.split_whitespace();
   |                 ---- immutable borrow occurs here
...
15 |     line.clear();
   |     ^^^^ mutable borrow occurs here
...
19 | }
   | - immutable borrow ends here

As由迭代器String拥有。解决方案描述为:

let parts: Vec<String> = line.split_whitespace()
    .map(|s| String::from(s))
    .collect();

我在这里有几个问题:

  1. 我已经通过调用每个迭代器来消耗迭代器。它的借用应该已经结束了。
  2. 我如何从函数定义中知道借用的生命周期?
  3. 如果一个函数正在借用一个对象,我怎么知道它释放了它?例如,在使用collect()释放借用的解决方案中。

我想我在这里遗漏了一个重要的概念。

4

1 回答 1

2

您的代码中的问题是您将结果绑定line.split_whitespace()到名称 ( parts)。如果你改写这个:

io::stdin().read_line(&mut line)
    .expect("Cannot read line.");

for p in line.split_whitespace() {   // <-- pass directly into loop
    println!("{}", p);
}

line.clear();
io::stdin().read_line(&mut line)
    .expect("Cannot read line.");

这样它就可以工作了。另一种可能性是人为地限制 的生命周期parts,如下所示:

io::stdin().read_line(&mut line)
    .expect("Cannot read line.");

{
    let parts = line.split_whitespace();

    for p in parts {
        println!("{}", p);
    }
}

line.clear();
io::stdin().read_line(&mut line)
    .expect("Cannot read line.");

这也有效。


那为什么呢?这是由于编译器当前的工作方式,通常称为“词法借用”。这里的问题是每个包含借用的非临时值在其范围结束之前都是“活动的”。

在您的情况下:由于您将split_whitespace()(借用字符串)的结果分配给parts,因此借用是“活动的”,直到parts. 直到生命的尽头parts

在这个答案的第一个版本中,我们没有将名称绑定到值,因此结果split_whitespace()只是临时的,并且借用不会扩展到整个范围。这也是您的collect()示例有效的原因:不是因为collect(),而是因为从来没有一个名称绑定到借用字符串的东西上。在我的第二个版本中,我们只是限制范围。

请注意,这是编译器的一个已知缺点。你是对的,编译器只是没有看到它。

于 2017-08-05T14:10:43.980 回答