使用`let`绑定来增加值的生命周期

Tho*_*hle 11 lifetime let rust

我编写了以下代码来读取整数数组stdin:

use std::io::{self, BufRead};

fn main() {
    let stdin = io::stdin();
    for line in stdin.lock().lines() {
        let xs: Vec<i32> = line.unwrap()
            .trim()
            .split(' ')
            .map(|s| s.parse().unwrap())
            .collect();

        println!("{:?}", xs);
    }
}
Run Code Online (Sandbox Code Playgroud)

这很好,但是,我感觉let xs线条有点长,所以我把它分成两部分:

use std::io::{self, BufRead};

fn main() {
    let stdin = io::stdin();
    for line in stdin.lock().lines() {
        let ss = line.unwrap().trim().split(' ');
        let xs: Vec<i32> = ss.map(|s| s.parse().unwrap()).collect();

        println!("{:?}", xs);
    }
}
Run Code Online (Sandbox Code Playgroud)

这没用!Rust回复了以下错误:

error[E0597]: borrowed value does not live long enough
  --> src/main.rs:6:18
   |
6  |         let ss = line.unwrap().trim().split(' ');
   |                  ^^^^^^^^^^^^^                  - temporary value dropped here while still borrowed
   |                  |
   |                  temporary value does not live long enough
...
10 |     }
   |     - temporary value needs to live until here
   |
   = note: consider using a `let` binding to increase its lifetime
Run Code Online (Sandbox Code Playgroud)

这让我很困惑.难道line还是ss不活得足够长?我怎样才能使用let绑定来延长其使用寿命?我以为我已经在使用了let

我已经阅读了一生的指南,但我仍然无法弄明白.任何人都可以给我一个提示吗?

Fra*_*gné 15

在您的第二个版本中,类型ssSplit<'a, char>.类型中的lifetime参数告诉我们该对象包含一个引用.为了使赋值有效,引用必须指向该语句之后存在的对象.然而,unwrap()消耗line; 换句话说,它将Ok变体的数据移出Result对象.因此,引用不指向原始内部line,而是指向临时对象.

在您的第一个版本中,您在long表达式的末尾使用临时值,尽管调用了map.要修复第二个版本,您需要绑定结果unwrap()以保持值足够长的时间:

use std::io::{self, BufRead};

fn main() {
    let stdin = io::stdin();
    for line in stdin.lock().lines() {
        let line = line.unwrap();
        let ss = line.trim().split(' ');
        let xs: Vec<i32> = ss.map(|s| s.parse().unwrap()).collect();

        println!("{:?}", xs);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 注意,这里有两个名为`line`的变量:第一个是`IoResult <String>`(=`Result <String,IoError>`),第二个是`String`.`unwrap()`*将`String`移出`IoResult`,之后`IoResult`无法使用(这也是为什么我重复使用'line`这个名称:你将无法无论如何使用第一个`line`).根本不复制`String`. (3认同)
  • `String`结构包含一个`Vec`,它包含一个指向数据的指针(存储在堆上),长度和容量.发生移动时,将复制struct的成员,但不会复制引用的数据,也不会运行析构函数.然后编译器使原始副本不可用(如果您尝试使用该值,则会收到错误),因此您不会访问已转移所有权的数据. (2认同)

snf*_*snf 5

这是关于unwrap()调用,它正在获取包含的对象,但此引用应该比容器对象的生命周期长,容器对象在下一行中超出范围(没有本地绑定)。

如果您想获得更清晰的代码,一种非常常见的编写方式是:

use std::io::{self, BufRead};

fn main() {
    let stdin = io::stdin();
    for line in stdin.lock().lines() {
        let xs: Vec<i32> = line.unwrap()
            .trim()
            .split(' ')
            .map(|s| s.parse().unwrap())
            .collect();

        println!("{:?}", xs);
    }
}
Run Code Online (Sandbox Code Playgroud)

如果没有,您可以创建到“解包”结果的绑定并使用它。