无法返回一个字符串切片的向量:借用的值没有足够长的生命周期

7
我是Rust的新手,borrow checker 让我有些困惑。我不明白为什么这段代码无法编译。如果这个问题已经被回答过了,那么很抱歉,但我在其他问题中找不到解决方案。
我知道这与将本地字符串作为切片返回(&str)相似,但在那种情况下,只返回了一个字符串,对我来说不足以理解我正在尝试返回向量的代码。据我所知,我正在尝试返回指向函数块结束时将超出范围的str类型的引用,因此应该将那个&str向量映射成一个String向量吗?我不太关心将&str转换为String的性能影响。首先,我只是想让它工作起来。
这是代码,错误在lex函数中。
use std::io::prelude::*;
use std::fs::File;
use std::env;

fn open(mut s: &mut String, filename: &String) {
    let mut f = match File::open(&filename) {
        Err(_) => panic!("Couldn't open file"),
        Ok(file) => file,
    };
    match f.read_to_string(&mut s) {
        Err(_) => panic!("Couldn't read file"),
        Ok(_) => println!("File read successfully"),
    };

}

fn lex(s: &String) -> Vec<&str> {
    let token_string: String = s.replace("(", " ( ")
        .replace(")", " ) ");

    let token_list: Vec<&str> = token_string.split_whitespace()
        .collect();
    token_list
}

fn main() {
    let args: Vec<_> = env::args().collect();
    if args.len() < 2 {
        panic!("Please provide a filename");
    } else {
        let ref filename = args[1];

        let mut s = String::new();
        open(&mut s, filename);
        let token_list: Vec<&str> = lex(&s);
        println!("{:?}", token_list);
    }
}

这里是错误消息

error: borrowed value does not live long enough
        self.0.borrow().values.get(idx)
        ^~~~~~~~~~~~~~~
reference must be valid for the anonymous lifetime #1 defined on the block at 23:54...
    pub fn value(&self, idx: usize) -> Option<&Value> {
                                                      ^
note: ...but borrowed value is only valid for the block at 23:54
    pub fn value(&self, idx: usize) -> Option<&Value> {
                                                      ^

我发现很难理解这段代码,因为我对Rust的经验有限,无法想象这些变量的生命周期。如果有帮助,将不胜感激,因为我已经花了一两个小时来尝试解决这个问题。


3
你是否查看过其他“x does not live long enough”问题的stackoverflow问答?在lex函数中,你创建了一个新的String,然后尝试返回该String中的引用,即使那个String并未超出lex函数的生命周期,所以你的引用将指向无效的内存。 - oli_obk
我需要在函数外定义字符串并传递可变引用吗?还是有一种方法可以延长函数内定义的字符串的生命周期,使其能够超越 lex 函数? - Alex G
1
你永远无法延长生命周期。我有一个解决方案,但如果你不想被标记为重复问题,你需要修改你的问题。它应该解释为什么它与所有其他询问如何返回拥有对象的引用的问题不同。 - oli_obk
1
重复的链接是https://dev59.com/Hl0b5IYBdhLWcg3wO_M_;但你可以花点时间让它变得不同。 - Shepmaster
1个回答

6
问题在于你在 lex 函数内部分配了一个新的 Stringtoken_string),然后返回对它的引用数组,但是 token_string 在函数结束时会超出作用域并释放内存。
fn lex(s: &String) -> Vec<&str> {
    let token_string: String = s.replace("(", " ( ") // <-- new String allocated 
        .replace(")", " ) "); 

    let token_list: Vec<&str> = token_string.split_whitespace()
        .collect();
    token_list // <-- this is just an array of wide pointers into token_string
} // <-- token_string gets freed here, so the returned pointers
  //     would be pointing to memory that's already been dropped!

有几种方法可以解决这个问题。一种方法是强制调用lex的人传递您想要使用的缓冲区来收集数据。这将更改签名为fn lex<'a>(input: &String, buffer: &'a mut String) -> Vec<&'a str>。此签名将指定返回的&str的生命周期至少与传入的缓冲区的生命周期相同。
另一种方法是如果您可以容忍额外的分配,则只返回Vec<String>而不是Vec<&str>

谢谢你的帮助。现在我明白了很多。 - Alex G

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