Commit fc2b38de authored by Benno Lossin's avatar Benno Lossin Committed by Danilo Krummrich
Browse files

add `[pin_]init_scope` to execute code before creating an initializer



In more complex cases, initializers need to run arbitrary code before
assigning initializers to fields. While this is possible using the
underscore codeblock feature (`_: {}`), values returned by such
functions cannot be used from later field initializers.

The two new functions `[pin_]init_scope` allow users to first run some
fallible code and then return an initializer which the function turns
into a single initializer. This permits using the same value multiple
times by different fields.

Reviewed-by: default avatarGary Guo <gary@garyguo.net>
Reviewed-by: default avatarDanilo Krummrich <dakr@kernel.org>
Signed-off-by: default avatarBenno Lossin <lossin@kernel.org>
Reviewed-by: default avatarAlice Ryhl <aliceryhl@google.com>
[ Fix typo in commit message: s/functinos/functions/. - Danilo ]
Signed-off-by: default avatarDanilo Krummrich <dakr@kernel.org>
parent e6901808
Loading
Loading
Loading
Loading
+87 −0
Original line number Diff line number Diff line
@@ -1392,6 +1392,93 @@ pub fn pin_init_array_from_fn<I, const N: usize, T, E>(
    unsafe { pin_init_from_closure(init) }
}

/// Construct an initializer in a closure and run it.
///
/// Returns an initializer that first runs the closure and then the initializer returned by it.
///
/// See also [`init_scope`].
///
/// # Examples
///
/// ```
/// # use pin_init::*;
/// # #[pin_data]
/// # struct Foo { a: u64, b: isize }
/// # struct Bar { a: u32, b: isize }
/// # fn lookup_bar() -> Result<Bar, Error> { todo!() }
/// # struct Error;
/// fn init_foo() -> impl PinInit<Foo, Error> {
///     pin_init_scope(|| {
///         let bar = lookup_bar()?;
///         Ok(try_pin_init!(Foo { a: bar.a.into(), b: bar.b }? Error))
///     })
/// }
/// ```
///
/// This initializer will first execute `lookup_bar()`, match on it, if it returned an error, the
/// initializer itself will fail with that error. If it returned `Ok`, then it will run the
/// initializer returned by the [`try_pin_init!`] invocation.
pub fn pin_init_scope<T, E, F, I>(make_init: F) -> impl PinInit<T, E>
where
    F: FnOnce() -> Result<I, E>,
    I: PinInit<T, E>,
{
    // SAFETY:
    // - If `make_init` returns `Err`, `Err` is returned and `slot` is completely uninitialized,
    // - If `make_init` returns `Ok`, safety requirement are fulfilled by `init.__pinned_init`.
    // - The safety requirements of `init.__pinned_init` are fulfilled, since it's being called
    //   from an initializer.
    unsafe {
        pin_init_from_closure(move |slot: *mut T| -> Result<(), E> {
            let init = make_init()?;
            init.__pinned_init(slot)
        })
    }
}

/// Construct an initializer in a closure and run it.
///
/// Returns an initializer that first runs the closure and then the initializer returned by it.
///
/// See also [`pin_init_scope`].
///
/// # Examples
///
/// ```
/// # use pin_init::*;
/// # struct Foo { a: u64, b: isize }
/// # struct Bar { a: u32, b: isize }
/// # fn lookup_bar() -> Result<Bar, Error> { todo!() }
/// # struct Error;
/// fn init_foo() -> impl Init<Foo, Error> {
///     init_scope(|| {
///         let bar = lookup_bar()?;
///         Ok(try_init!(Foo { a: bar.a.into(), b: bar.b }? Error))
///     })
/// }
/// ```
///
/// This initializer will first execute `lookup_bar()`, match on it, if it returned an error, the
/// initializer itself will fail with that error. If it returned `Ok`, then it will run the
/// initializer returned by the [`try_init!`] invocation.
pub fn init_scope<T, E, F, I>(make_init: F) -> impl Init<T, E>
where
    F: FnOnce() -> Result<I, E>,
    I: Init<T, E>,
{
    // SAFETY:
    // - If `make_init` returns `Err`, `Err` is returned and `slot` is completely uninitialized,
    // - If `make_init` returns `Ok`, safety requirement are fulfilled by `init.__init`.
    // - The safety requirements of `init.__init` are fulfilled, since it's being called from an
    //   initializer.
    unsafe {
        init_from_closure(move |slot: *mut T| -> Result<(), E> {
            let init = make_init()?;
            init.__init(slot)
        })
    }
}

// SAFETY: the `__init` function always returns `Ok(())` and initializes every field of `slot`.
unsafe impl<T> Init<T> for T {
    unsafe fn __init(self, slot: *mut T) -> Result<(), Infallible> {