我想用一个函数来初始化一个大对象。目前我有:
fn initialize(mydata: &mut Vec<Vec<MyStruct>>) { /* ... */ }
我更喜欢有:
fn initialize() -> Vec<Vec<MyStruct>> { /* ... */ }
我听说C++经常实现返回值优化(RVO),如果你运气好并拥有一个好的编译器。我们能不能在这里禁用复制,并通过传递到函数中的隐藏指针返回它?RVO是语言的一部分还是可选优化?
我想用一个函数来初始化一个大对象。目前我有:
fn initialize(mydata: &mut Vec<Vec<MyStruct>>) { /* ... */ }
我更喜欢有:
fn initialize() -> Vec<Vec<MyStruct>> { /* ... */ }
我听说C++经常实现返回值优化(RVO),如果你运气好并拥有一个好的编译器。我们能不能在这里禁用复制,并通过传递到函数中的隐藏指针返回它?RVO是语言的一部分还是可选优化?
没错,无论如何,你都应该写下来。
fn initialize() -> Vec<Vec<MyStruct>> { ... }
顺便说一下,Vec
不是很大——只有 3 个指针大小的整数。
Rust 具有 RVO,并且在 指南中进行了宣传。你可以使用以下代码自己查看:
#[inline(never)]
fn initialize() -> Vec<i32> {
Vec::new()
}
fn main() {
let v = initialize();
}
如果您在Playground上以发布模式编译此程序并输出汇编码,除其他所有内容外,您将看到以下信息:
playground::initialize:
movq $4, (%rdi)
xorps %xmm0, %xmm0
movups %xmm0, 8(%rdi)
retq
Vec::new()
被内联了,但是你可以看出其思想 - 新的Vec
实例的地址通过%rdi
传递到函数中,函数直接将Vec
字段存储到该内存中,避免了不必要的堆栈复制。这是调用方式:
playground::main:
subq $24, %rsp
movq %rsp, %rdi
callq playground::initialize
您可以看到最终Vec
实例将直接放入堆栈内存中。
define internal fastcc void @initialize(%"struct.collections::vec::Vec<[i32]>[#3]"* noalias nocapture sret dereferenceable(24)) unnamed_addr #0
。LLVM IR对于声明类似于C语言,因此该函数返回void
并且取一个指针(*
)到一个 struct.collections::vec::Vec<[i32]>
。(我使用#[no_mangle]
使其更清晰。) - huon
initialize
函数。 - Michael