考虑片段
struct Foo {
dummy: [u8; 65536],
}
fn bar(foo: Foo) {
println!("{:p}", &foo)
}
fn main() {
let o = Foo { dummy: [42u8; 65536] };
println!("{:p}", &o);
bar(o);
}
程序的典型结果是
0x7fffc1239890
0x7fffc1229890
其中地址不同。
显然,正如编译器的move实现中所预期的那样,大数组dummy
已经被复制。不幸的是,由于dummy
是一个非常大的数组,这可能会对性能产生不小的影响。这种影响可能会迫使人们选择通过引用传递参数,即使函数实际上在概念上"消耗"了参数。
由于Foo
不导出Copy
,因此对象o
被移动。既然Rust禁止访问移动的对象,是什么阻止了bar
"重用"原始对象o
,迫使编译器生成一个潜在的昂贵的逐位副本?是否存在根本性的困难,或者我们会看到编译器有一天会优化掉这个逐位副本吗?
在Rust中(与C或C++不同),值的地址并不重要,因此在语言中没有任何东西可以阻止副本的省略。
然而,如今rustc并没有优化任何东西:所有的优化都委托给LLVM,而且您似乎在这里遇到了LLVM优化器的限制(不清楚这个限制是由于LLVM接近C的语义还是只是一个遗漏)。
因此,有两种改进代码生成的途径:
- 教导LLVM执行此优化(如果可能)
- 教导rustc执行此优化(既然rustc有了MIR,优化过程就要到了)
但现在您可能只是想避免在堆栈上分配这么大的对象,例如可以Box
。