dtolnay / request-for-implementation

Crates that don't exist, but should
610 stars 6 forks source link

Safe placement new as a library #46

Open crlf0710 opened 4 years ago

crlf0710 commented 4 years ago

Assume we have

struct S{ a: u32 }
struct T { s: S, t: [u32; 5] }
fn foo(x: bool) -> u32 { ... }

and let mut v = MaybeUninit::uninit(); design a macro called init, when invoked as init!(v, T { s: S { a: f1 }, t: [f2; 5] }, f1 = foo(true), f2 = foo(false));, it gets expanded into:

{
   let f1 = foo(true);
   let f2 = foo(false);
   let v_mut_ptr = v.as_mut_ptr();
   unsafe {
       // FIXME: static assert T can be initialized by initializing "s" and "t"
       let s_mut_ptr = &mut (*v_mut_ptr).s as * mut S;
       // FIXME: static assert S can be initialized by initializing "a"
       std::ptr::write(&mut (*s_mut_ptr).a, f1);
       std::ptr::write(&mut (*v_mut_ptr).t, [f2; 5]);
   }
}

The key points is that there's no panicking possibility within the unsafe region. And compiler will properly check about the Copy-ability of f2.

This serves as basis of higher-level macros like boxed , boxed!(S {a: f1}, f1 = foo(true)) get expanded into:

{
    let mut b = Box::new_uninit();
    init!(b.as_mut(), S {a: f1}, f1 = foo(true));
    unsafe { b.assume_init() }
}
SeeSpring commented 4 years ago

I have a implementation at https://github.com/SeeSpring/placement_new_macros. I'm not sure that there's a way to make this safe since if the user passes in a packed struct the macro will create unaligned references.