为什么Rust既有按值传递又有按引用传递?

3
一些编程语言,例如Haskell,不区分值传递和引用传递。编译器可以通过启发式算法近似选择最有效的调用约定。例如,对于Linux x64 ABI,一个启发式算法是:如果参数大小大于16字节,则将指针传递到堆栈中;否则在寄存器中传递值。
在Rust中保留传值和传引用(当然不能改变)的两个概念并强制用户进行选择,有什么优点?
是不是情况这样:如果发现一个值被修改,传值就成了传引用 + 拷贝的语法糖?
2个回答

12

两件事:

  1. Rust会根据类似的启发式方法,将某些传值调用转换为传引用。
  2. 传值表示所有权转移,而传引用表示借用。这两者非常不同,且与你所问的汇编级别问题完全无关。

换句话说,在Rust中,这两种形式具有不同的语义。但并不排除做优化的可能性。


我同意第二点。但这似乎是类型检查的层面,所以在类型检查之后,代码生成是否可以相同? - Łukasz Lew
@ŁukaszLew:是的,尽管它是否确实这样做是另一个完全不同的问题 :) - Matthieu M.

0

[编辑:更改示例以在发布模式下工作]

这不是语法糖,因为可以通过查看生成的代码来看出。

给定这些函数:

fn by_value(v: (u64, u64)) -> u64 {
  v.0 + v.1
}

fn by_ref(v: &(u64, u64)) -> u64 {
  v.0 + v.1
}

然后,如果其中一个是另一个的语法糖,我们期望它们生成相同的汇编代码,或者至少具有相同的调用约定。但实际上,我们发现 by_ref 通过 rdirsi 寄存器传递 v,而 by_value 则在 rdi 寄存器中传递指向 v 的指针,并且必须跟随该指针才能获取该值:(详细信息,请使用发布模式)。

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

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

1
你似乎没有使用优化进行编译。使用您代码的此版本(请注意所有旋转以防止优化器完全删除函数),并在发布模式下编译,两个函数都编译成汇编leaq 1(%rdi), %rax; retq。请注意,关于推送和弹出寄存器的所有垃圾都已被优化掉。 - Shepmaster
你是对的,在优化模式下它们变得相同。我现在已经更改为稍微复杂一些的示例,以显示不同的行为。 - rp123
最近的更改中,你没有在进行苹果对苹果的比较。检查值的大小显示一个由两个u64组成的元组是16字节,而引用则是8字节。优化器很可能已经决定8字节比16字节更好。 - Shepmaster
不再了。现在都使用leaq。 - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接