我曾认为一旦对象被移动,其在堆栈上占用的内存就可以被重用于其他目的。但是,下面的最简例子显示相反。
#[inline(never)]
fn consume_string(s: String) {
drop(s);
}
fn main() {
println!(
"String occupies {} bytes on the stack.",
std::mem::size_of::<String>()
);
let s = String::from("hello");
println!("s at {:p}", &s);
consume_string(s);
let r = String::from("world");
println!("r at {:p}", &r);
consume_string(r);
}
在我的计算机上使用--release
标志编译代码后,它会输出以下内容。
String occupies 24 bytes on the stack.
s at 0x7ffee3b011b0
r at 0x7ffee3b011c8
很明显,即使移动了s
,r
也不会重用原本属于s
的24字节堆栈块。我认为重用移动对象的堆栈内存是安全的,但为什么Rust编译器不这样做呢?我有遗漏任何角落吗?
更新:
如果我用花括号将s
包围起来,r
可以重用堆栈上的24字节块。
#[inline(never)]
fn consume_string(s: String) {
drop(s);
}
fn main() {
println!(
"String occupies {} bytes on the stack.",
std::mem::size_of::<String>()
);
{
let s = String::from("hello");
println!("s at {:p}", &s);
consume_string(s);
}
let r = String::from("world");
println!("r at {:p}", &r);
consume_string(r);
}
以上代码会得到以下输出结果。
String occupies 24 bytes on the stack.
s at 0x7ffee2ca31f8
r at 0x7ffee2ca31f8
我认为花括号不应该有任何影响,因为在调用comsume_string(s)
后s
的生命周期已经结束,其drop handler在comsume_string()
内被调用。为什么添加花括号可以启用优化?
我使用的Rust编译器版本如下。rustc 1.54.0-nightly (5c0292654 2021-05-11)
binary: rustc
commit-hash: 5c029265465301fe9cb3960ce2a5da6c99b8dcf2
commit-date: 2021-05-11
host: x86_64-apple-darwin
release: 1.54.0-nightly
LLVM version: 12.0.1
更新 2:
我想澄清一下我的问题重点。我想知道提出的“堆栈重用优化”属于哪个类别。
- 这是一种无效的优化。在某些情况下,如果我们执行“优化”,编译后的代码可能会失败。
- 这是一种有效的优化,但编译器(包括rustc前端和llvm)无法执行它。
- 这是一种有效的优化,但被暂时关闭了,就像这里。
- 这是一种有效的优化,但被遗漏了。将来会添加。
std::string
已经“死亡”,因为移动后的值始终有效。 - Masklinn.push_str()
强制 String 实例占用栈空间 (2) 没有可观察到的行为被改变,因为没有可观察到的东西 (3) 这个空间在s
结束其生命周期后不会被重复使用。 - Zhiyao