rust

How to allocate structs on the heap without taking up space on the stack in stable Rust?


In this code...

struct Test { a: i32, b: i64 }
    
fn foo() -> Box<Test> {              // Stack frame:
    let v = Test { a: 123, b: 456 }; // 12 bytes
    Box::new(v)                      // `usize` bytes (`*const T` in `Box`)
}

... as far as I understand (ignoring possible optimizations), v gets allocated on the stack and then copied to the heap, before being returned in a Box.

And this code...

fn foo() -> Box<Test> {
    Box::new(Test { a: 123, b: 456 })
}

...shouldn't be any different, presumably, since there should be a temporary variable for struct allocation (assuming compiler doesn't have any special semantics for the instantiation expression inside Box::new()).

I've found Do values in return position always get allocated in the parents stack frame or receiving Box?. Regarding my specific question, it only proposes the experimental box syntax, but mostly talks about compiler optimizations (copy elision).

So my question remains: using stable Rust, how does one allocate structs directly on the heap, without relying on compiler optimizations?


Solution

  • As of Rust 1.39, there seems to be only one way in stable to allocate memory on the heap directly - by using std::alloc::alloc (note that the docs state that it is expected to be deprecated). It's reasonably unsafe.

    Example:

    #[derive(Debug)]
    struct Test {
        a: i64,
        b: &'static str,
    }
    
    fn main() {
        use std::alloc::{alloc, dealloc, Layout};
    
        unsafe {
            let layout = Layout::new::<Test>();
            let ptr = alloc(layout) as *mut Test;
    
            (*ptr).a = 42;
            (*ptr).b = "testing";
    
            let bx = Box::from_raw(ptr);
    
            println!("{:?}", bx);
        }
    }
    

    This approach is used in the unstable method Box::new_uninit.

    It turns out there's even a crate for avoiding memcpy calls (among other things): copyless. This crate also uses an approach based on this.