【问题标题】:"Borrowed value does not live long enough", dropped when used in a loop“借来的价值不够长”,在循环中使用时被丢弃
【发布时间】:2021-02-11 20:06:52
【问题描述】:

我知道当loop 的范围结束并且向量input 包含trimmed_text 的切片时,String 会被删除。

我想解决方案是将这些切片的所有权转移到input 或类似的东西。如何做到这一点?

use std::io;

fn main() {
    let mut input: Vec<&str>;

    loop {
        let mut input_text = String::new();
        println!("Type instruction in the format Add <name> to <department>:");
        io::stdin()
            .read_line(&mut input_text)
            .expect("failed to read from stdin");
        let trimmed_text: String = input_text.trim().to_string();

        input = trimmed_text.split(" ").collect();

        if input[0] == "Add" && input[2] == "to" {
            break;
        } else {
            println!("Invalid format.");
        }
    }

    println!("{:?}", input);
}

编译错误:

error[E0597]: `trimmed_text` does not live long enough
  --> src/main.rs:14:17
   |
14 |         input = trimmed_text.split(" ").collect();
   |                 ^^^^^^^^^^^^ borrowed value does not live long enough
...
21 |     }
   |     - `trimmed_text` dropped here while still borrowed
22 | 
23 |     println!("{:?}", input);
   |                      ----- borrow later used here

【问题讨论】:

  • 我认为只需将您的 input 从持有 &amp;str 更改为拥有的字符串就可以了。您可能还需要将修剪和拆分的文本显式复制到 input
  • 除了改变类型外,还需要显式地将字符串切片转为字符串,例如使用.map(String::from) 或类似的。

标签: string loops rust lifetime borrow-checker


【解决方案1】:

.split() 返回对 String 的引用,该引用在循环结束时被删除,但您希望 input 超过循环结束,因此您应该重构它以保存拥有的值而不是引用.示例:

use std::io;

fn example() {
    let mut input: Vec<String>; // changed from &str to String

    loop {
        let mut input_text = String::new();
        println!("Type instruction in the format Add <name> to <department>:");
        io::stdin()
            .read_line(&mut input_text)
            .expect("failed to read from stdin");

        // map str refs into owned Strings
        input = input_text.trim().split(" ").map(String::from).collect();

        if input[0] == "Add" && input[2] == "to" {
            break;
        } else {
            println!("Invalid format.");
        }
    }

    println!("{:?}", input);
}

playground

【讨论】:

  • 更正。 .split() 返回对 str 的引用。不是String
【解决方案2】:

正如您所了解的,问题在于切片的所有者寿命不够长。您没有指出(并且可能没有明确看到)的是所有者是变量trimmed_text。所以你想要做的(如果你不想复制每个切片以获得更好的性能)就是让trimmed_text的范围更大:

use std::io;

fn main() {
    let mut input: Vec<&str>;
    let mut trimmed_text: String;

    loop {
        ...
        trimmed_text = input_text.trim().to_string();

        input = trimmed_text.split(" ").collect();

        if input[0] == "Add" && input[2] == "to" {
            break;
        } else {
            ...
        }
    }

    println!("{:?}", input);
}

在这里,我们解决了切片所有者生命周期的错误。但是,我们还有第二个问题:

13 |         trimmed_text = input_text.trim().to_string();
   |         ^^^^^^^^^^^^ assignment to borrowed `trimmed_text` occurs here
14 | 
15 |         input = trimmed_text.split(" ").collect();
   |         -----   ------------ borrow of `trimmed_text` occurs here
   |         |
   |         borrow might be used here, when `input` is dropped and runs the `Drop` code for type `std::vec::Vec`

这告诉我们,在循环之后,input 变量在修改后仍然借用trimmed_text。为了解决这个问题,我们可以缩小输入的范围,使输入的范围不包含第 13 行:

use std::io;

fn main() {
    // Remove the "let mut input: Vec<&str>;"
    let mut trimmed_text: String;

    loop {
        ...
        trimmed_text = input_text.trim().to_string();

        let input: Vec<&str> = trimmed_text.split(" ").collect();

        ...
    }
}

如果我们不在循环之外使用input,这工作得很好。现在我们只需要在循环结束时输出input 的值即可:

use std::io;

fn main() {
    // Remove the "let mut input: Vec<&str>;"
    let mut trimmed_text: String;

    let results = loop {
        ...
        trimmed_text = input_text.trim().to_string();

        let input: Vec<&str> = trimmed_text.split(" ").collect();

        if input[0] == "Add" && input[2] == "to" {
            break input;
        }
        ...
    };

    println!("{:?}", results);
}

我们有它!

TL;DR:

这是一个无需复制切片的代码:

use std::io;

fn main() {
    let mut trimmed_text: String;

    let results = loop {
        let mut input_text = String::new();
        println!("Type instruction in the format Add <name> to <department>:");
        io::stdin()
            .read_line(&mut input_text)
            .expect("failed to read from stdin");
        trimmed_text = input_text.trim().to_string();

        let input: Vec<&str> = trimmed_text.split(" ").collect();

        if input[0] == "Add" && input[2] == "to" {
            break input;
        } else {
            println!("Invalid format.");
        }
    };

    println!("{:?}", results);
}

我将input 重命名为results 以避免与循环内的input 变量混淆。如果您真的愿意,请随时将其重命名为 input

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-07-23
    • 1970-01-01
    • 2019-11-29
    • 2015-04-12
    • 2014-12-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多