bal*_*ve2 2 string char ownership rust borrow-checker
我的失败代码(最小的、可重现的示例):
// 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
}
}
}
Run Code Online (Sandbox Code Playgroud)
编译器:“借用时临时值下降”。下面有红色波浪线
ARRAY[i].to_string()
Run Code Online (Sandbox Code Playgroud)
我猜是经典的借用检查器问题?
我尝试将类型更改为&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
}
}
Run Code Online (Sandbox Code Playgroud)
无法弄清楚有什么特别之处char或者转换to_string()使得此代码失败。
您的失败代码相当于以下代码:
for a in &ARRAY {
let temp = a.to_string();
list.push(Text::new(&temp));
drop(temp);
}
Run Code Online (Sandbox Code Playgroud)
除了临时变量在我的代码中有一个名称(对的调用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();
}
Run Code Online (Sandbox Code Playgroud)
您的第二个代码之所以有效,是因为现在您的数组包含静态引用&'static str,因此任何地方都没有临时变量。