diff options
| author | Benno Lossin <lossin@kernel.org> | 2025-10-16 23:05:39 +0200 |
|---|---|---|
| committer | Danilo Krummrich <dakr@kernel.org> | 2025-10-21 00:40:12 +0200 |
| commit | fc2b38de4c01710ecb9ebb9ecdce9a7bf433e9a8 (patch) | |
| tree | 1c21a203d799fa2402e60803665a949e11cebd49 /rust/pin-init/src | |
| parent | e6901808a3b28d8bdabfa98a618b2eab6f8798e8 (diff) | |
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: Gary Guo <gary@garyguo.net>
Reviewed-by: Danilo Krummrich <dakr@kernel.org>
Signed-off-by: Benno Lossin <lossin@kernel.org>
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
[ Fix typo in commit message: s/functinos/functions/. - Danilo ]
Signed-off-by: Danilo Krummrich <dakr@kernel.org>
Diffstat (limited to 'rust/pin-init/src')
| -rw-r--r-- | rust/pin-init/src/lib.rs | 87 |
1 files changed, 87 insertions, 0 deletions
diff --git a/rust/pin-init/src/lib.rs b/rust/pin-init/src/lib.rs index dd553212836e..8dc9dd5ac6fd 100644 --- a/rust/pin-init/src/lib.rs +++ b/rust/pin-init/src/lib.rs @@ -1392,6 +1392,93 @@ where 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> { |