【问题标题】:Why does Rust have both call by value and call by reference?为什么 Rust 同时具有按值调用和按引用调用?
【发布时间】:2016-04-12 01:52:25
【问题描述】:

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

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

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

【问题讨论】:

    标签: rust calling-convention


    【解决方案1】:

    两件事:

    1. Rust 将基于类似的启发式方法将某些按值传递调用转换为按引用传递。
    2. 按值传递表示所有权转移,而按引用传递表示借用。这些与您所询问的 asm 级问题非常不同,并且完全正交。

    换句话说,在 Rust 中,这两种形式具有不同的语义。不过,这并不排除也进行优化。

    【讨论】:

    • 我同意 2。但这似乎是类型检查级别,所以在类型检查之后 - 代码生成可以相同吗?
    • @ŁukaszLew:是的,虽然它是否确实完全是另一个问题:)
    【解决方案2】:

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

    它不是语法糖,通过查看生成的代码可以看出。

    鉴于这些功能:

    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 寄存器中传递了一个指向v 的指针,并且必须遵循该指针才能获得值:(see details,使用释放模式):

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

    【讨论】:

    • 您似乎没有进行优化编译。使用this version of your code(注意所有旋转以防止优化器完全删除函数)和在发布模式下编译,这两个函数都编译为程序集leaq 1(%rdi), %rax; retq。请注意,所有关于推送和弹出寄存器的垃圾都已被优化掉。
    • 你是对的,在优化模式下它们变得相同。我现在已更改为显示不同行为的稍微复杂的示例。
    • 通过最近的更改,您不是在比较苹果与苹果。 Checking the size of the values 表示两个 u64 的元组是 16 个字节,引用是 8 个字节。优化器完全有可能认为 8 字节优于 16 字节。
    猜你喜欢
    • 1970-01-01
    • 2021-03-18
    • 2021-12-21
    • 1970-01-01
    • 2013-08-15
    • 1970-01-01
    相关资源
    最近更新 更多