char 到 string 转换的所有权问题

Ownership problem with char to string conversion

我的失败代码(Minimal, Reproducible Example):

// my code

fn test() {
    let mut list: Vec<Text> = Vec::new();
    const ARRAY: [char; 3] = ['a', 'b', 'c'];
    for (i, _) in ARRAY.iter().enumerate() {
        list.push(Text::new(&ARRAY[i].to_string()));
    }
}

// external crate

#[derive(Debug, Clone, Copy, PartialEq)]
struct Text<'a> {
    pub text: &'a str,
}

impl<'a> Text<'a> {
    pub fn new(text: &'a str) -> Self {
        Text {
            text
        }
    }
}

编译器:“借用时删除了临时值”。下面的红色波浪线

ARRAY[i].to_string()

我猜是经典借用检查器问题?

我尝试将类型更改为 &strs 而不是 chars 并且一切正常:

// my code
fn test() {
    let mut list: Vec<Text> = Vec::new();
    const ARRAY: [&str; 3] = ["a", "b", "c"]; // CHANGE HERE
    for (i, _) in ARRAY.iter().enumerate() {
        list.push(Text::new(&ARRAY[i])); // CHANGE HERE
    }
}

无法弄清楚 char 或使用 to_string() 的转换有何特别之处导致此代码失败。

特别之处在于 to_string() 创建一个拥有的字符串,而 "..." 给你一个 &'static str。拥有的字符串有一个明确的生命周期,当它释放存储数据的内存时,它会在其作用域结束时结束。您尝试将 references 存储到此类字符串,并在字符串已被释放后保留它们。

外部板条箱中的 Text 类型旨在 借用 其他人拥有的数据。如果你不能改变它,你只需要确保你拥有的值至少与 Text 值一样长。例如:

fn test() {
    let mut list: Vec<Text> = Vec::new();
    const ARRAY: [char; 3] = ['a', 'b', 'c'];
    // keep the strings live
    let strings: Vec<String> = ARRAY.iter().map(|c| c.to_string()).collect();
    // `Text`s in `list` refer to strings in `strings`
    for s in &strings {
        list.push(Text::new(s));
    }
}

您的失败代码等同于此代码:

for a in &ARRAY {
    let temp = a.to_string();
    list.push(Text::new(&temp));
    drop(temp);
}

除了临时变量在我的代码中有一个名称(对 drop() 的调用是多余的,为了清楚起见我添加了它)。

所以在循环期间创建的所有临时对象都作为引用添加到列表中,请记住 Text 持有一个引用,而不是字符串本身。但是这些临时对象不会在创建它们的迭代中存活下来,因此您的列表包含悬空引用。 Rust 检测到并无法编译。

简单的解决方案是修改 Text 以保存 String 本身,而不是引用。如果你不能修改它,那么你的字符串必须存在于某个地方,你可以构建一个 Vec<String> 并从那里获取引用:

fn test() {
    const ARRAY: [char; 3] = ['a', 'b', 'c'];
    let mut tmps: Vec<String> = Vec::new();
    for a in &ARRAY {
        tmps.push(a.to_string());
    }
    let list: Vec<Text> = tmps
        .iter()
        .map(|s| Text::new(s))
        .collect();
}

您的第二个代码之所以有效,是因为现在您的数组包含静态引用 &'static str,因此任何地方都没有临时变量。