Hello everyone,
I am currently working on improving the initialization ergonomics found in the Rust for Linux project. I have finished the general design and added essential comments. I would like to get some feedback on the approach, especially on the soundness, ergonomics and documentation.
Here is an example from the Rust-for-Linux project:
impl SharedState {
fn try_new() -> Result<Ref<Self>> {
let mut state = Pin::from(UniqueRef::try_new(Self {
// SAFETY: `condvar_init!` is called below.
state_changed: unsafe { CondVar::new() },
// SAFETY: `mutex_init!` is called below.
inner: unsafe { Mutex::new(SharedStateInner { token_count: 0 }) },
})?);
// SAFETY: `state_changed` is pinned when `state` is.
let pinned = unsafe { state.as_mut().map_unchecked_mut(|s| &mut s.state_changed) };
kernel::condvar_init!(pinned, "SharedState::state_changed");
// SAFETY: `inner` is pinned when `state` is.
let pinned = unsafe { state.as_mut().map_unchecked_mut(|s| &mut s.inner) };
kernel::mutex_init!(pinned, "SharedState::inner");
Ok(state.into())
}
}
with my library:
impl SharedState {
fn try_new() -> Result<Ref<Self>> {
let state = Pin::from(UniqueRef::try_new(core::mem::MaybeUninit::<Self>::uninit())?);
let state = kernel::init! {state => SharedState {
kernel::init_with_lockdep!(.state_changed, "SharedState::state_changed");
kernel::init_with_lockdep!(.inner, "SharedState::inner", SharedStateInner { token_count: 0 });
}};
Ok(state.into())
}
}
Here is a small rundown of how the macro works:
- use raw pointers to initialize the fields of the struct
- direct access is simple
- function/macro calls get a special pointer (
InitMe<'_, FieldType, Guard>
) that has library support. Theinit!
macro then expects a return type ofInitProof<R, Guard>
whereR
will be propagated outwards andGuard
is a special guard parameter that was also present on theInitMe
. It is essential for the soundness that anInitProof
can only be constructed, if initialization was also successful. The guard parameter ensures that one cannot misuse a differentInitProof
to spoof the macro.
- use a struct initializer to ensure that each field was initialized exactly once
- "
assume_init
"
Thank you for your feedback!