当堆栈分配值被装箱时会发生什么?

时间:2015-04-13 22:07:34

标签: rust boxing

如果我们有一个已经在堆栈上分配的值,那么装箱会将它复制到堆中然后转移所有权(这是如何在.NET中运行的,除了两个副本都能保持活动状态)?或者编译器是" smart"足以从一开始就直接在堆上分配它?

struct Foo {
    x: i32,
}

fn main() {
    // a is allocated on stack?
    let a = Foo { x: 1 };

    // if a is not used, it will be optimized out
    println!("{}", a.x);

    // what happens here? will the stack allocated structure
    // be moved to heap? or was it originally allocated on heap?
    let b = Box::new(a);
}

我不是汇编程序的专家,但看起来它实际上是在堆栈上分配然后移动:http://pastebin.com/8PzsgTJ1。但我需要一个确实知道发生了什么的人的确认。

2 个答案:

答案 0 :(得分:3)

如你所描述的那样,这种优化会发生,这很奇怪。例如,在此代码中:

let a = Foo { x: 1 };
// operation that observes a
let b = Box::new(a);
// operation that observes b

&a&b 相等,这会令人惊讶。但是,如果你做了类似的事情,但不要注意a

#[inline(never)]
fn frobnotz() -> Box<Foo> {
    let a = Foo { x: 1 };
    Box::new(a)
}

您可以see via the LLVM IR此案例 优化:

define internal fastcc noalias dereferenceable(4) %Foo* @_ZN8frobnotz20h3dca7bc0ee8400bciaaE() unnamed_addr #0 {
entry-block:
  %0 = tail call i8* @je_mallocx(i64 4, i32 0)
  %1 = icmp eq i8* %0, null
  br i1 %1, label %then-block-106-.i.i, label %"_ZN5boxed12Box$LT$T$GT$3new20h2665038481379993400E.exit"

then-block-106-.i.i:                              ; preds = %entry-block
  tail call void @_ZN3oom20he7076b57c17ed7c6HYaE()
  unreachable

"_ZN5boxed12Box$LT$T$GT$3new20h2665038481379993400E.exit": ; preds = %entry-block
  %2 = bitcast i8* %0 to %Foo*
  %x.sroa.0.0..sroa_idx.i = bitcast i8* %0 to i32*
  store i32 1, i32* %x.sroa.0.0..sroa_idx.i, align 4
  ret %Foo* %2
}

同样,您可以在堆栈上返回结构,然后将其打包,仍然会just be the one allocation

  

您可能认为这给我们带来了糟糕的表现:返回一个值,然后立即将其打包?!难道这种模式不是两个世界中最糟糕的吗? Rust比那更聪明。此代码中没有副本。 main为盒子分配足够的空间,将指向该内存的指针传递给foo作为x,然后foo将值直接写入Box。

答案 1 :(得分:2)

正如官方Rust文档here中所解释的那样,Box<T>::new(x: T)在堆上分配内存,然后参数移动到该内存中。在a之后访问let b = Box::new(a)是编译时错误。