为什么 Rust 同时具有按值调用和按引用调用?

Why does Rust have both call by value and call by reference?

有些语言,例如 Haskell,不区分按值传递和按引用传递。然后,编译器可以通过启发式方法大致选择最有效的调用约定。一个启发式示例是针对 Linux x64 ABI:如果参数的大小大于 16 字节,则传递一个指向堆栈的指针,否则传递寄存器中的值。

在 Rust 中同时保留按值传递和按引用传递(当然是不可变的)概念并强制用户选择有什么好处?

难道传值是传引用+复制的语法糖,如果看到值被修改了?

两件事:

  1. Rust 会根据类似的启发式将某些按值传递调用转换为按引用传递。
  2. 按值传递表示所有权转移,而按引用传递表示借用。这些与您所询问的 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_refrdirsi 寄存器中传递了 v,而 by_valuerdi 注册并必须跟随该指针获取值:(see details,使用释放模式):

by_value:
  movq  8(%rdi), %rax
  addq  (%rdi), %rax
  retq

by_ref:
  leaq  (%rdi,%rsi), %rax
  retq