为什么 Rust 同时具有按值调用和按引用调用?
Why does Rust have both call by value and call by reference?
有些语言,例如 Haskell,不区分按值传递和按引用传递。然后,编译器可以通过启发式方法大致选择最有效的调用约定。一个启发式示例是针对 Linux x64 ABI:如果参数的大小大于 16 字节,则传递一个指向堆栈的指针,否则传递寄存器中的值。
在 Rust 中同时保留按值传递和按引用传递(当然是不可变的)概念并强制用户选择有什么好处?
难道传值是传引用+复制的语法糖,如果看到值被修改了?
两件事:
- Rust 会根据类似的启发式将某些按值传递调用转换为按引用传递。
- 按值传递表示所有权转移,而按引用传递表示借用。这些与您所询问的 asm 级问题非常不同,并且完全正交。
换句话说,在Rust中,这两种形式具有不同的语义。不过,这并不排除也进行优化。
[已编辑:将示例更改为在发布模式下工作]
它不是语法糖,正如查看生成的代码所见。
给出这些函数:
fn by_value(v: (u64, u64)) -> u64 {
v.0 + v.1
}
fn by_ref(v: &(u64, u64)) -> u64 {
v.0 + v.1
}
那么如果一个是另一个的语法糖,我们希望它们生成相同的汇编代码,或者至少是相同的调用约定。但实际上,我们发现 by_ref
在 rdi
和 rsi
寄存器中传递了 v
,而 by_value
在 rdi
注册并必须跟随该指针获取值:(see details,使用释放模式):
by_value:
movq 8(%rdi), %rax
addq (%rdi), %rax
retq
by_ref:
leaq (%rdi,%rsi), %rax
retq
有些语言,例如 Haskell,不区分按值传递和按引用传递。然后,编译器可以通过启发式方法大致选择最有效的调用约定。一个启发式示例是针对 Linux x64 ABI:如果参数的大小大于 16 字节,则传递一个指向堆栈的指针,否则传递寄存器中的值。
在 Rust 中同时保留按值传递和按引用传递(当然是不可变的)概念并强制用户选择有什么好处?
难道传值是传引用+复制的语法糖,如果看到值被修改了?
两件事:
- Rust 会根据类似的启发式将某些按值传递调用转换为按引用传递。
- 按值传递表示所有权转移,而按引用传递表示借用。这些与您所询问的 asm 级问题非常不同,并且完全正交。
换句话说,在Rust中,这两种形式具有不同的语义。不过,这并不排除也进行优化。
[已编辑:将示例更改为在发布模式下工作]
它不是语法糖,正如查看生成的代码所见。
给出这些函数:
fn by_value(v: (u64, u64)) -> u64 {
v.0 + v.1
}
fn by_ref(v: &(u64, u64)) -> u64 {
v.0 + v.1
}
那么如果一个是另一个的语法糖,我们希望它们生成相同的汇编代码,或者至少是相同的调用约定。但实际上,我们发现 by_ref
在 rdi
和 rsi
寄存器中传递了 v
,而 by_value
在 rdi
注册并必须跟随该指针获取值:(see details,使用释放模式):
by_value:
movq 8(%rdi), %rax
addq (%rdi), %rax
retq
by_ref:
leaq (%rdi,%rsi), %rax
retq