Skip to content

Commit 84abc02

Browse files
authored
Merge pull request #86 from Rust-for-Linux/dev/init-scope
add `[pin_]init_scope` to execute code before creating an initializer
2 parents 871cf88 + 9fb0e4e commit 84abc02

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_pin_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)