Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JamesTheAwesomeDude/8def02afe4d95673c6cbfc708b3cc08f to your computer and use it in GitHub Desktop.
Save JamesTheAwesomeDude/8def02afe4d95673c6cbfc708b3cc08f to your computer and use it in GitHub Desktop.
Rust write to fields of MaybeUninit struct/array
/* Example Usage:
#[repr(C,packed)]
struct Foo {
data1: [u8; 3],
data2: [u8; 5]
}
let baz: Foo = {
let mut baz_wip = core::mem::MaybeUninit::<Foo>::uninit();
// 1. Writing fields:
// Works to initialize fields
maybeuninit_write!(&!mut baz_wip.data1, [42; 3]);
maybeuninit_write!(&!mut baz_wip.data2, [69; 5]);
// 2. Reading fields:
// UB IF FIELD IS NOT ALREADY INITIALIZED
{
let x = maybeuninit_assume_init_read!(baz_wip.data2);
println!("{:#?}", x);
}
// 3. Borrowing reference to fields:
// UB IF FIELD IS NOT ALREADY INITIALIZED
{
let y: &_ = maybeuninit_assume_init_getref!(&baz_wip.data2);
println!("{:#?}", y);
}
// 4. Borrowing mut reference to fields:
// UB IF FIELD IS NOT ALREADY INITIALIZED
{
let z: &mut _ = maybeuninit_assume_init_getmut!(&mut baz_wip.data2);
*z = [99; 5];
}
unsafe { baz_wip.assume_init() };
};
*/
macro_rules! maybeuninit_write {
( &!mut $obj:tt . $field:tt , $val:expr ) => {
{
// https://doc.rust-lang.org/beta/core/ptr/macro.addr_of_mut.html#:~:text=Correct%20usage:%20Creating%20a%20pointer%20to%20uninitialized%20data
let obj = &mut $obj;
let ptr = obj.as_mut_ptr();
let fptr = unsafe { core::ptr::addr_of_mut!( (*ptr).$field ) };
let val = $val;
#[inline]
unsafe fn write_with_lifetime<'a, T, _U>(fptr: *mut T, val: T, _obj: &'a mut _U) -> &'a mut T {
// TODO pending https://github.com/rust-lang/rust/issues/96284
fptr.write_unaligned(val);
// TODO/FIXME: lifetime constraint is too aggressive, locking down the whole struct
&mut *fptr
}
unsafe { write_with_lifetime(fptr, val, obj) }
}
};
( &!mut $obj:tt [ $index:expr ] , $val:expr ) => {
{
// TODO pending https://github.com/rust-lang/rust/issues/96097
let obj = &mut $obj;
let ptr = obj.as_mut_ptr();
let index = $index;
let fptr = unsafe { core::ptr::addr_of_mut!( (*ptr)[index] ) };
let val = $val;
#[inline]
unsafe fn write_with_lifetime<'a, T, _U>(fptr: *mut T, val: T, _obj: &'a mut _U) -> &'a mut T {
// TODO pending https://github.com/rust-lang/rust/issues/96284
fptr.write_unaligned(val);
// TODO/FIXME: lifetime constraint is too aggressive, locking down the whole array
&mut *fptr
}
unsafe { write_with_lifetime(fptr, val, obj) }
}
}
}
macro_rules! maybeuninit_assume_init_getmut {
( &mut $obj:tt . $field:tt ) => {
{
let obj = &mut $obj;
let ptr = obj.as_mut_ptr();
let fptr = unsafe { core::ptr::addr_of_mut!( (*ptr).$field ) };
#[inline]
unsafe fn getmut_with_lifetime<'a, T, _U>(fptr: *mut T, _obj: &'a mut _U) -> &'a mut T {
// TODO/FIXME: lifetime constraint is too aggressive, locking down the whole struct
&mut *fptr
}
unsafe { getmut_with_lifetime(fptr, obj) }
}
};
( &mut $obj:tt [ $index:expr ] ) => {
{
let obj = &mut $obj;
let ptr = obj.as_mut_ptr();
let index = $index;
let fptr = unsafe { core::ptr::addr_of_mut!( (*ptr)[index] ) };
#[inline]
unsafe fn getmut_with_lifetime<'a, T, _U>(fptr: *mut T, _obj: &'a mut _U) -> &'a mut T {
// TODO/FIXME: lifetime constraint is too aggressive, locking down the whole array
&mut *fptr
}
unsafe { getmut_with_lifetime(fptr, obj) }
}
}
}
macro_rules! maybeuninit_assume_init_getref {
( & $obj:tt . $field:tt ) => {
{
let obj = &$obj;
let ptr = obj.as_ptr();
let fptr = unsafe { core::ptr::addr_of!( (*ptr).$field ) };
#[inline]
unsafe fn getref_with_lifetime<'a, T, _U>(fptr: *const T, _obj: &'a _U) -> &'a T {
// TODO/FIXME: lifetime constraint is too aggressive, locking down the whole struct
& *fptr
}
unsafe { getref_with_lifetime(fptr, obj) }
}
};
( & $obj:tt [ $index:expr ] ) => {
{
// TODO pending https://github.com/rust-lang/rust/issues/96097
let obj = &$obj;
let ptr = obj.as_ptr();
let index = $index;
let fptr = unsafe { core::ptr::addr_of!( (*ptr)[index] ) };
#[inline]
unsafe fn getref_with_lifetime<'a, T, _U>(fptr: *const T, _obj: &'a _U) -> &'a T {
// TODO/FIXME: lifetime constraint is too aggressive, locking down the whole array
& *fptr
}
unsafe { getref_with_lifetime(fptr, obj) }
}
}
}
macro_rules! maybeuninit_assume_init_read {
( $obj:tt . $field:tt ) => {
{
let ptr = (&$obj).as_ptr();
let fptr = unsafe { core::ptr::addr_of!( (*ptr).$field ) };
unsafe { fptr.read() }
}
};
( $obj:tt [ $index:expr ] ) => {
{
// TODO pending https://github.com/rust-lang/rust/issues/96097
let ptr = (&$obj).as_ptr();
let index = $index;
let fptr = unsafe { core::ptr::addr_of!( (*ptr)[index] ) };
unsafe { fptr.read() }
}
}
}
@JamesTheAwesomeDude
Copy link
Author

Feature request: needs to be recursive

// FIXME this should be allowed
let foo_wip = MaybeUninit<[Foo; 1024]>;
maybeuninit_write!(&!mut foo_wip[42].data1, 69);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment