借用的值未能存活到足够长的时间,在循环中使用时被丢弃

3

我知道当loop的作用域结束时,String会被删除,而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

我认为只需将您的“input”从持有“&str”更改为拥有字符串即可解决问题。您可能还需要将修剪和分割的文本明确复制到“input”中。 - Ted Klein Bergman
除了更改类型之外,您需要明确地将字符串切片转换为字符串,例如使用.map(String::from)或类似方法。 - Sven Marnach
2个回答

8

.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);
}

游乐场


更正一下,.split() 返回的是 str 的引用,而不是 String - Boss Man

1

正如你所理解的那样,问题在于切片的所有者寿命不够长。你没有指出(也许没有明确看到)的是,所有者是变量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,因为它被修改了。为了解决这个问题,我们可以缩小 input 的作用域,使得它的作用域不包含第 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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接