我来自C++,对于这段Rust代码的合法性感到相当惊讶:
let x = &mut String::new();
x.push_str("Hello!");
C++中,你不能获取临时对象的地址,并且临时对象不会在它出现的表达式结束后继续存在。
Rust中临时对象能存活多久?因为x
只是一个借用,所以字符串的所有权归谁?
为什么借用临时变量是合法的?
和在C++中不合法的原因一样,因为有人说应该这样做。
表达式的临时作用域是包含该表达式的最小范围之一,可以是以下几种情况之一:在Rust中临时变量存活多久?由于
x
只是一个借用,那么字符串的所有者是谁?
if
、while
或loop
表达式的主体。if
表达式的else
块。if
或while
表达式的条件表达式或match
守卫。match
分支的表达式。本质上,您可以将您的代码视为:
let mut a_variable_you_cant_see = String::new();
let x = &mut a_variable_you_cant_see;
x.push_str("Hello!");
参见:
来自Rust参考手册:
临时生命周期
在大多数表达式上下文中使用值表达式时,将创建一个临时的未命名内存位置,该位置初始化为该值,并且表达式将评估为该位置
这适用于此处,因为String::new()
是一个值表达式,并且正好位于&mut
下方,处于放置表达式上下文中。现在引用运算符只需要通过这个临时内存位置,因此它成为整个右侧(包括&mut
)的值。
然而,当正在创建分配给let声明的临时值表达式时,临时值将使用封闭块的生命周期创建
由于它被分配给变量,因此它具有直到封闭块结束的生命周期。
这也回答了这个问题关于以下两种情况之间的区别:
let a = &String::from("abcdefg"); // ok!
并且
let a = String::from("abcdefg").as_str(); // compile error
as_str()
中,因此它的生命周期在语句结束时结束。let a
声明都会遮蔽先前的声明。 - sateeshfn main() {
let foo = &String::new();
}
以及它所产生的MIR(标准评论已被我替换为我的):
fn main() -> () {
let mut _0: ();
scope 1 {
let _1: &std::string::String; // the reference is declared
}
scope 2 {
}
let mut _2: std::string::String; // the owner is declared
bb0: {
StorageLive(_1); // the reference becomes applicable
StorageLive(_2); // the owner becomes applicable
_2 = const std::string::String::new() -> bb1; // the owner gets a value; go to basic block 1
}
bb1: {
_1 = &_2; // the reference now points to the owner
_0 = ();
StorageDead(_1); // the reference is no longer applicable
drop(_2) -> bb2; // the owner's value is dropped; go to basic block 2
}
bb2: {
StorageDead(_2); // the owner is no longer applicable
return;
}
}
_0
元组只是 main()
函数的返回值;虽然不是最有趣的信息,但也有其价值 :)。 - ljedrz
&x
取x
的地址,我认为这对于临时变量来说是无效的。我应该将其与创建对临时变量的引用进行比较,这确实是可能的,甚至可以扩展临时变量的寿命,因此总体行为实际上与 Rust 所做的非常相似。 - Sven Marnachstruct T { T* me() { return this; } };
将返回T
实例的地址,无论它是否是临时的。此外,C++允许将const引用或r-value引用绑定到临时变量,并且引用只是一个伪装成指针的小东西。 - Matthieu M.error[E0716]
error E0716: temporary value dropped while borrowed (rust) 提出了简明扼要的问题。它链接回这个问题。 - JamesThomasMoon