rust/pin-init/src/lib.rs | 87 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 87 insertions(+)
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 functinos `[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>
---
rust/pin-init/src/lib.rs | 87 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 87 insertions(+)
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 @@ 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> {
base-commit: 3a8660878839faadb4f1a6dd72c3179c1df56787
--
2.51.0
On Thu Oct 16, 2025 at 11:05 PM CEST, Benno Lossin wrote:
> 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 functinos `[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>
Applied to driver-core-testing, thanks!
[ Fix typo in commit message: s/functinos/functions/. - Danilo ]
Benno, thanks a lot for providing this patch as prerequisite for [1]; your quick
support with pin-init whenever something comes up is much appreciated!
[1] https://lore.kernel.org/all/20251016125544.15559-1-dakr@kernel.org/
On Thu, Oct 16, 2025 at 11:05:39PM +0200, Benno Lossin wrote:
> 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 functinos `[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>
© 2016 - 2026 Red Hat, Inc.