rust-for-linux.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] add `[pin_]init_scope` to execute code before creating an initializer
@ 2025-10-16 21:05 Benno Lossin
  2025-10-17 13:08 ` Alice Ryhl
  2025-10-21 17:43 ` Danilo Krummrich
  0 siblings, 2 replies; 3+ messages in thread
From: Benno Lossin @ 2025-10-16 21:05 UTC (permalink / raw)
  To: Benno Lossin, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Danilo Krummrich, Fiona Behrens, Christian Schrefl
  Cc: rust-for-linux, linux-kernel

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


^ permalink raw reply related	[flat|nested] 3+ messages in thread

* Re: [PATCH] add `[pin_]init_scope` to execute code before creating an initializer
  2025-10-16 21:05 [PATCH] add `[pin_]init_scope` to execute code before creating an initializer Benno Lossin
@ 2025-10-17 13:08 ` Alice Ryhl
  2025-10-21 17:43 ` Danilo Krummrich
  1 sibling, 0 replies; 3+ messages in thread
From: Alice Ryhl @ 2025-10-17 13:08 UTC (permalink / raw)
  To: Benno Lossin
  Cc: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	Björn Roy Baron, Andreas Hindborg, Trevor Gross,
	Danilo Krummrich, Fiona Behrens, Christian Schrefl,
	rust-for-linux, linux-kernel

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>

^ permalink raw reply	[flat|nested] 3+ messages in thread

* Re: [PATCH] add `[pin_]init_scope` to execute code before creating an initializer
  2025-10-16 21:05 [PATCH] add `[pin_]init_scope` to execute code before creating an initializer Benno Lossin
  2025-10-17 13:08 ` Alice Ryhl
@ 2025-10-21 17:43 ` Danilo Krummrich
  1 sibling, 0 replies; 3+ messages in thread
From: Danilo Krummrich @ 2025-10-21 17:43 UTC (permalink / raw)
  To: Benno Lossin
  Cc: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
	Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
	Fiona Behrens, Christian Schrefl, rust-for-linux, linux-kernel

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/

^ permalink raw reply	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2025-10-21 17:43 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-10-16 21:05 [PATCH] add `[pin_]init_scope` to execute code before creating an initializer Benno Lossin
2025-10-17 13:08 ` Alice Ryhl
2025-10-21 17:43 ` Danilo Krummrich

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).