Skip to content

Commit 91f324f

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 <[email protected]>
1 parent 43f0424 commit 91f324f

File tree

3 files changed

+116
-0
lines changed

3 files changed

+116
-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: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1392,6 +1392,89 @@ 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+
// - initialization delegated to a valid initializer,
1428+
// - initializer called by another initializer.
1429+
unsafe {
1430+
pin_init_from_closure(move |slot: *mut T| -> Result<(), E> {
1431+
let init = make_init()?;
1432+
init.__pinned_init(slot)
1433+
})
1434+
}
1435+
}
1436+
1437+
/// Construct an initializer in a closure and run it.
1438+
///
1439+
/// Returns an initializer that first runs the closure and then the initializer returned by it.
1440+
///
1441+
/// See also [pin_init_scope].
1442+
///
1443+
/// # Examples
1444+
///
1445+
/// ```
1446+
/// # use pin_init::*;
1447+
/// # struct Foo { a: u64, b: isize }
1448+
/// # struct Bar { a: u32, b: isize }
1449+
/// # fn lookup_bar() -> Result<Bar, Error> { todo!() }
1450+
/// # struct Error;
1451+
/// fn init_foo() -> impl Init<Foo, Error> {
1452+
/// init_scope(|| {
1453+
/// let bar = lookup_bar()?;
1454+
/// Ok(try_init!(Foo { a: bar.a.into(), b: bar.b }? Error))
1455+
/// })
1456+
/// }
1457+
/// ```
1458+
///
1459+
/// This initializer will first execute `lookup_bar()`, match on it, if it returned an error, the
1460+
/// initializer itself will fail with that error. If it returned `Ok`, then it will run the
1461+
/// initializer returned by the `try_init!` invocation.
1462+
pub fn init_scope<T, E, F, I>(make_init: F) -> impl Init<T, E>
1463+
where
1464+
F: FnOnce() -> Result<I, E>,
1465+
I: Init<T, E>,
1466+
{
1467+
// SAFETY:
1468+
// - initialization delegated to a valid initializer,
1469+
// - initializer called by another initializer.
1470+
unsafe {
1471+
init_from_closure(move |slot: *mut T| -> Result<(), E> {
1472+
let init = make_init()?;
1473+
init.__init(slot)
1474+
})
1475+
}
1476+
}
1477+
13951478
// SAFETY: the `__init` function always returns `Ok(())` and initializes every field of `slot`.
13961479
unsafe impl<T> Init<T> for T {
13971480
unsafe fn __init(self, slot: *mut T) -> Result<(), Infallible> {

tests/init-scope.rs

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

0 commit comments

Comments
 (0)