"Borrowed value does not live long enough",在循环中使用时丢弃

"Borrowed value does not live long enough", dropped when used in a loop

我知道 Stringloop 的范围结束时被删除并且向量 input 包含 trimmed_text.

的切片

我想解决方案是将这些切片的所有权移至 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

.split() returns 对 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

如您所知,问题是切片的所有者寿命不够长。您没有指出(并且可能没有明确看到)的是所有者是变量 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);
}

我们找到了!

长话短说:

这是一个无需复制切片即可执行所需操作的代码:

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