Skip to content

Commit a90f441

Browse files
committed
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 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. Signed-off-by: Benno Lossin <lossin@kernel.org>
1 parent b8d1b64 commit a90f441

File tree

3 files changed

+121
-0
lines changed

3 files changed

+121
-0
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- `[pin_]init_scope` functions to run arbitrary code inside of an initializer.
13+
1014
### Changed
1115

1216
- `#[pin_data]` now generates a `*Projection` struct similar to the `pin-project` crate.

src/lib.rs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1392,6 +1392,93 @@ where
13921392
unsafe { pin_init_from_closure(init) }
13931393
}
13941394

1395+
/// Construct an initializer in a closure and run it.
1396+
///
1397+
/// Returns an initializer that first runs the closure and then the initializer returned by it.
1398+
///
1399+
/// See also [init_scope].
1400+
///
1401+
/// # Examples
1402+
///
1403+
/// ```
1404+
/// # use pin_init::*;
1405+
/// # #[pin_data]
1406+
/// # struct Foo { a: u64, b: isize }
1407+
/// # struct Bar { a: u32, b: isize }
1408+
/// # fn lookup_bar() -> Result<Bar, Error> { todo!() }
1409+
/// # struct Error;
1410+
/// fn init_foo() -> impl PinInit<Foo, Error> {
1411+
/// pin_init_scope(|| {
1412+
/// let bar = lookup_bar()?;
1413+
/// Ok(try_pin_init!(Foo { a: bar.a.into(), b: bar.b }? Error))
1414+
/// })
1415+
/// }
1416+
/// ```
1417+
///
1418+
/// This initializer will first execute `lookup_bar()`, match on it, if it returned an error, the
1419+
/// initializer itself will fail with that error. If it returned `Ok`, then it will run the
1420+
/// initializer returned by the `try_init!` invocation.
1421+
pub fn pin_init_scope<T, E, F, I>(make_init: F) -> impl PinInit<T, E>
1422+
where
1423+
F: FnOnce() -> Result<I, E>,
1424+
I: PinInit<T, E>,
1425+
{
1426+
// SAFETY:
1427+
// - If `make_init` returns `Err`, `Err` is returned and `slot` is completely uninitialized,
1428+
// - If `make_init` returns `Ok`, safety requirement are fulfilled by `init.__pinned_init`.
1429+
// - The safety requirements of `init.__pinned_init` are fulfilled, since it's being called
1430+
// from an initializer.
1431+
unsafe {
1432+
pin_init_from_closure(move |slot: *mut T| -> Result<(), E> {
1433+
let init = make_init()?;
1434+
init.__pinned_init(slot)
1435+
})
1436+
}
1437+
}
1438+
1439+
/// Construct an initializer in a closure and run it.
1440+
///
1441+
/// Returns an initializer that first runs the closure and then the initializer returned by it.
1442+
///
1443+
/// See also [pin_init_scope].
1444+
///
1445+
/// # Examples
1446+
///
1447+
/// ```
1448+
/// # use pin_init::*;
1449+
/// # struct Foo { a: u64, b: isize }
1450+
/// # struct Bar { a: u32, b: isize }
1451+
/// # fn lookup_bar() -> Result<Bar, Error> { todo!() }
1452+
/// # struct Error;
1453+
/// fn init_foo() -> impl Init<Foo, Error> {
1454+
/// init_scope(|| {
1455+
/// let bar = lookup_bar()?;
1456+
/// Ok(try_init!(Foo { a: bar.a.into(), b: bar.b }? Error))
1457+
/// })
1458+
/// }
1459+
/// ```
1460+
///
1461+
/// This initializer will first execute `lookup_bar()`, match on it, if it returned an error, the
1462+
/// initializer itself will fail with that error. If it returned `Ok`, then it will run the
1463+
/// initializer returned by the `try_init!` invocation.
1464+
pub fn init_scope<T, E, F, I>(make_init: F) -> impl Init<T, E>
1465+
where
1466+
F: FnOnce() -> Result<I, E>,
1467+
I: Init<T, E>,
1468+
{
1469+
// SAFETY:
1470+
// - If `make_init` returns `Err`, `Err` is returned and `slot` is completely uninitialized,
1471+
// - If `make_init` returns `Ok`, safety requirement are fulfilled by `init.__init`.
1472+
// - The safety requirements of `init.__init` are fulfilled, since it's being called from an
1473+
// initializer.
1474+
unsafe {
1475+
init_from_closure(move |slot: *mut T| -> Result<(), E> {
1476+
let init = make_init()?;
1477+
init.__init(slot)
1478+
})
1479+
}
1480+
}
1481+
13951482
// SAFETY: the `__init` function always returns `Ok(())` and initializes every field of `slot`.
13961483
unsafe impl<T> Init<T> for T {
13971484
unsafe fn __init(self, slot: *mut T) -> Result<(), Infallible> {

tests/init-scope.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#![allow(dead_code)]
2+
#![cfg_attr(not(RUSTC_LINT_REASONS_IS_STABLE), feature(lint_reasons))]
3+
4+
use pin_init::*;
5+
6+
#[pin_data]
7+
pub struct MyStruct {
8+
a: usize,
9+
b: isize,
10+
}
11+
12+
fn foo() -> Result<usize, ()> {
13+
Ok(0)
14+
}
15+
16+
impl MyStruct {
17+
pub fn new() -> impl Init<Self, ()> {
18+
init_scope(|| {
19+
let a = foo()?;
20+
Ok(try_init!(Self { a, b: 42 }?()))
21+
})
22+
}
23+
24+
pub fn new2() -> impl PinInit<Self, ()> {
25+
pin_init_scope(|| {
26+
let a = foo()?;
27+
Ok(try_pin_init!(Self { a, b: 42 }?()))
28+
})
29+
}
30+
}

0 commit comments

Comments
 (0)