在我下面的示例中,cons.push(...)
是否复制了 self
参数?
或者 rustc
是否足够智能,可以意识到来自 #a
和 #b
行的值始终可以使用相同的堆栈空间,并且不需要复制(明显的 i32
副本除外)?
换句话说,在移动所有权时,调用 Cons.push(self, ...)
是否总是创建 self
的副本?还是 self
结构始终留在堆栈中?
对文档的引用将不胜感激。
#[derive(Debug)]
struct Cons<T, U>(T, U);
impl<T, U> Cons<T, U> {
fn push<V>(self, value: V) -> Cons<Self, V> {
Cons(self, value)
}
}
fn main() {
let cons = Cons(1, 2); // #a
let cons = cons.push(3); // #b
println!("{:?}", cons); // #c
}
我上面例子中的含义是,每次我们添加像 #b
这样的行时,push(...)
函数的调用成本是否会增加O(n^2)
的速率(如果 self
每次都被复制)或者以 O(n)
的速率(如果 self
留在原地)。
我尝试实现 Drop
特性并注意到 #a
和 #b
都被丢弃了 after #c
。对我来说,这似乎表明 self
在此示例中保持不变,但我不是 100%。
最佳答案
总的来说,相信编译器! Rust + LLVM 是一个非常强大的组合,通常会产生出奇高效的代码。随着时间的推移,它会改进得更多。
In other words, does a call to Cons.push(self, ...) always create a copy of self as ownership is being moved? Or does the self struct always stay in place on the stack?
self
不能留在原地,因为 push
返回的新值方法的类型为 Cons<Self, V>
,它本质上是一个元组 Self
和 V
.虽然tuples don't have any memory layout guarantees ,我坚信他们的元素不能任意分散在内存中。因此,self
和 value
两者都必须移入新结构。
以上段落假设self
在调用 push
之前被牢牢地放在堆栈上.编译器实际上有足够的信息知道它应该为最终结构保留足够的空间。特别是对于函数内联,这很可能成为一种优化。
The implication in my example above is whether or not the push(...) function grows more expensive to call each time we add a line like #b at the rate of O(n^2) (if self is copied each time) or at the rate of O(n) (if self stays in place).
考虑两个函数 ( playground ):
pub fn push_int(cons: Cons<i32, i32>, x: i32) -> Cons<Cons<i32, i32>, i32> {
cons.push(x)
}
pub fn push_int_again(
cons: Cons<Cons<i32, i32>, i32>,
x: i32,
) -> Cons<Cons<Cons<i32, i32>, i32>, i32> {
cons.push(x)
}
push_int
将第三个元素添加到 Cons
和 push_int_again
添加第四个元素。
push_int
在 Release模式下编译为以下程序集:
movq %rdi, %rax
movl %esi, (%rdi)
movl %edx, 4(%rdi)
movl %ecx, 8(%rdi)
retq
和push_int_again
编译为:
movq %rdi, %rax
movl 8(%rsi), %ecx
movl %ecx, 8(%rdi)
movq (%rsi), %rcx
movq %rcx, (%rdi)
movl %edx, 12(%rdi)
retq
您不需要了解汇编就可以看出压入第四个元素比压入第三个元素需要更多的指令。
请注意,此观察是针对这些功能单独进行的。像cons.push(x).push(y).push(...)
这样的电话是内联的,并且程序集随着每次推送一条指令而线性增长。
关于rust - 调用函数时移动所有权是否会复制 `self` 结构?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57011967/