Skip to content

Commit 1258aa4

Browse files
committed
add pin_chain and chain functions
These functions allow providing a closure to change a value after initialization.
1 parent 3bbeb7e commit 1258aa4

4 files changed

Lines changed: 186 additions & 28 deletions

File tree

src/__internal.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use super::*;
1313
///
1414
/// [nomicon]: https://doc.rust-lang.org/nomicon/subtyping.html
1515
/// [this table]: https://doc.rust-lang.org/nomicon/phantom-data.html#table-of-phantomdata-patterns
16-
type Invariant<T> = PhantomData<fn(*mut T) -> *mut T>;
16+
pub(crate) type Invariant<T> = PhantomData<fn(*mut T) -> *mut T>;
1717

1818
/// This is the module-internal type implementing `PinInit` and `Init`. It is unsafe to create this
1919
/// type, since the closure needs to fulfill the same safety requirement as the
@@ -32,6 +32,18 @@ where
3232
}
3333
}
3434

35+
// SAFETY: While constructing the `InitClosure`, the user promised that it upholds the
36+
// `__pinned_init` invariants.
37+
unsafe impl<T: ?Sized, F, E> PinInit<T, E> for InitClosure<F, T, E>
38+
where
39+
F: FnOnce(*mut T) -> Result<(), E>,
40+
{
41+
#[inline]
42+
unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> {
43+
(self.0)(slot)
44+
}
45+
}
46+
3547
/// This trait is only implemented via the `#[pin_data]` proc-macro. It is used to facilitate
3648
/// the pin projections within the initializers.
3749
///

src/lib.rs

Lines changed: 147 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -850,6 +850,80 @@ pub unsafe trait PinInit<T: ?Sized, E = Infallible>: Sized {
850850
/// deallocate.
851851
/// - `slot` will not move until it is dropped, i.e. it will be pinned.
852852
unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E>;
853+
854+
/// First initializes the value using `self` then calls the function `f` with the initialized
855+
/// value.
856+
///
857+
/// If `f` returns an error the value is dropped and the initializer will forward the error.
858+
///
859+
/// # Examples
860+
///
861+
/// ```rust
862+
/// # #![allow(clippy::disallowed_names)]
863+
/// # use core::{convert::Infallible, pin::Pin};
864+
/// # use pinned_init::*;
865+
/// use pinned_init::pin_init_from_closure;
866+
/// #[repr(C)]
867+
/// struct RawFoo([u8; 16]);
868+
/// extern {
869+
/// fn init_foo(_: *mut RawFoo);
870+
/// }
871+
///
872+
/// #[pin_data]
873+
/// struct Foo {
874+
/// #[pin]
875+
/// raw: RawFoo,
876+
/// }
877+
///
878+
/// impl Foo {
879+
/// fn setup(self: Pin<&mut Self>) {
880+
/// println!("Setting up foo");
881+
/// }
882+
/// }
883+
///
884+
/// let foo = pin_init!(Foo {
885+
/// raw <- unsafe { pin_init_from_closure(|slot| {
886+
/// init_foo(slot);
887+
/// Ok::<_, Infallible>(())
888+
/// }) },
889+
/// }).pin_chain(|foo| {
890+
/// foo.setup();
891+
/// Ok(())
892+
/// });
893+
/// ```
894+
fn pin_chain<F>(self, f: F) -> ChainPinInit<Self, F, T, E>
895+
where
896+
F: FnOnce(Pin<&mut T>) -> Result<(), E>,
897+
{
898+
ChainPinInit(self, f, PhantomData)
899+
}
900+
}
901+
902+
/// An initializer returned by [`PinInit::pin_chain`].
903+
pub struct ChainPinInit<I, F, T: ?Sized, E>(I, F, __internal::Invariant<(E, Box<T>)>);
904+
905+
// SAFETY: The `__pinned_init` function is implemented such that it
906+
// - returns `Ok(())` on successful initialization,
907+
// - returns `Err(err)` on error and in this case `slot` will be dropped.
908+
// - considers `slot` pinned.
909+
unsafe impl<T: ?Sized, E, I, F> PinInit<T, E> for ChainPinInit<I, F, T, E>
910+
where
911+
I: PinInit<T, E>,
912+
F: FnOnce(Pin<&mut T>) -> Result<(), E>,
913+
{
914+
unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> {
915+
// SAFETY: All requirements fulfilled since this function is `__pinned_init`.
916+
unsafe { self.0.__pinned_init(slot)? };
917+
// SAFETY: The above call initialized `slot` and we still have unique access.
918+
let val = unsafe { &mut *slot };
919+
// SAFETY: `slot` is considered pinned.
920+
let val = unsafe { Pin::new_unchecked(val) };
921+
(self.1)(val).map_err(|e| {
922+
// SAFETY: `slot` was initialized above.
923+
unsafe { core::ptr::drop_in_place(slot) };
924+
e
925+
})
926+
}
853927
}
854928

855929
/// An initializer for `T`.
@@ -882,7 +956,7 @@ pub unsafe trait PinInit<T: ?Sized, E = Infallible>: Sized {
882956
///
883957
/// [`Arc<T>`]: alloc::sync::Arc
884958
#[must_use = "An initializer must be used in order to create its value."]
885-
pub unsafe trait Init<T: ?Sized, E = Infallible>: Sized {
959+
pub unsafe trait Init<T: ?Sized, E = Infallible>: PinInit<T, E> {
886960
/// Initializes `slot`.
887961
///
888962
/// # Safety
@@ -891,16 +965,75 @@ pub unsafe trait Init<T: ?Sized, E = Infallible>: Sized {
891965
/// - the caller does not touch `slot` when `Err` is returned, they are only permitted to
892966
/// deallocate.
893967
unsafe fn __init(self, slot: *mut T) -> Result<(), E>;
968+
969+
/// First initializes the value using `self` then calls the function `f` with the initialized
970+
/// value.
971+
///
972+
/// If `f` returns an error the value is dropped and the initializer will forward the error.
973+
///
974+
/// # Examples
975+
///
976+
/// ```rust
977+
/// # #![allow(clippy::disallowed_names)]
978+
/// # use pinned_init::*;
979+
/// # use core::convert::Infallible;
980+
/// use pinned_init::{self, init_from_closure};
981+
/// struct Foo {
982+
/// buf: [u8; 1_000_000],
983+
/// }
984+
///
985+
/// impl Foo {
986+
/// fn setup(&mut self) {
987+
/// println!("Setting up foo");
988+
/// }
989+
/// }
990+
///
991+
/// let foo = init!(Foo {
992+
/// buf <- zeroed::<_, Infallible>()
993+
/// }).chain(|foo| {
994+
/// foo.setup();
995+
/// Ok(())
996+
/// });
997+
/// ```
998+
fn chain<F>(self, f: F) -> ChainInit<Self, F, T, E>
999+
where
1000+
F: FnOnce(&mut T) -> Result<(), E>,
1001+
{
1002+
ChainInit(self, f, PhantomData)
1003+
}
1004+
}
1005+
1006+
/// An initializer returned by [`Init::chain`].
1007+
pub struct ChainInit<I, F, T: ?Sized, E>(I, F, __internal::Invariant<(E, Box<T>)>);
1008+
1009+
// SAFETY: The `__init` function is implemented such that it
1010+
// - returns `Ok(())` on successful initialization,
1011+
// - returns `Err(err)` on error and in this case `slot` will be dropped.
1012+
unsafe impl<T: ?Sized, E, I, F> Init<T, E> for ChainInit<I, F, T, E>
1013+
where
1014+
I: Init<T, E>,
1015+
F: FnOnce(&mut T) -> Result<(), E>,
1016+
{
1017+
unsafe fn __init(self, slot: *mut T) -> Result<(), E> {
1018+
// SAFETY: All requirements fulfilled since this function is `__init`.
1019+
unsafe { self.0.__pinned_init(slot)? };
1020+
// SAFETY: The above call initialized `slot` and we still have unique access.
1021+
(self.1)(unsafe { &mut *slot }).map_err(|e| {
1022+
// SAFETY: `slot` was initialized above.
1023+
unsafe { core::ptr::drop_in_place(slot) };
1024+
e
1025+
})
1026+
}
8941027
}
8951028

896-
// SAFETY: Every in-place initializer can also be used as a pin-initializer.
897-
unsafe impl<T: ?Sized, E, I> PinInit<T, E> for I
1029+
// SAFETY: `__pinned_init` behaves exactly the same as `__init`.
1030+
unsafe impl<T: ?Sized, E, I, F> PinInit<T, E> for ChainInit<I, F, T, E>
8981031
where
8991032
I: Init<T, E>,
1033+
F: FnOnce(&mut T) -> Result<(), E>,
9001034
{
9011035
unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> {
902-
// SAFETY: `__init` meets the same requirements as `__pinned_init`, except that it does not
903-
// require `slot` to not move after init.
1036+
// SAFETY: `__init` has less strict requirements compared to `__pinned_init`.
9041037
unsafe { self.__init(slot) }
9051038
}
9061039
}
@@ -953,13 +1086,20 @@ pub fn uninit<T, E>() -> impl Init<MaybeUninit<T>, E> {
9531086
}
9541087

9551088
// SAFETY: Every type can be initialized by-value.
956-
unsafe impl<T> Init<T> for T {
957-
unsafe fn __init(self, slot: *mut T) -> Result<(), Infallible> {
1089+
unsafe impl<T, E> Init<T, E> for T {
1090+
unsafe fn __init(self, slot: *mut T) -> Result<(), E> {
9581091
unsafe { slot.write(self) };
9591092
Ok(())
9601093
}
9611094
}
9621095

1096+
// SAFETY: Every type can be initialized by-value. `__pinned_init` calls `__init`.
1097+
unsafe impl<T, E> PinInit<T, E> for T {
1098+
unsafe fn __pinned_init(self, slot: *mut T) -> Result<(), E> {
1099+
unsafe { self.__init(slot) }
1100+
}
1101+
}
1102+
9631103
/// Smart pointer that can initialize memory in-place.
9641104
pub trait InPlaceInit<T>: Sized {
9651105
/// Use the given pin-initializer to pin-initialize a `T` inside of a new smart pointer of this

tests/ring_buf.rs

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -44,11 +44,17 @@ impl<T, const SIZE: usize> RingBuffer<T, SIZE> {
4444
// SAFETY: `this` is a valid pointer.
4545
head: unsafe { addr_of_mut!((*this.as_ptr()).buffer).cast::<T>() },
4646
tail: unsafe { addr_of_mut!((*this.as_ptr()).buffer).cast::<T>() },
47-
_pin <- PhantomPinned,
47+
_pin: PhantomPinned,
4848
})
4949
}
5050

51-
pub fn push<E>(self: Pin<&mut Self>, value: impl Init<T, E>) -> Result<bool, E> {
51+
pub fn push(self: Pin<&mut Self>, value: impl Init<T>) -> bool {
52+
match self.try_push(value) {
53+
Ok(res) => res,
54+
Err(i) => match i {},
55+
}
56+
}
57+
pub fn try_push<E>(self: Pin<&mut Self>, value: impl Init<T, E>) -> Result<bool, E> {
5258
// SAFETY: We do not move `this`.
5359
let this = unsafe { self.get_unchecked_mut() };
5460
let next_head = unsafe { this.advance(this.head) };
@@ -111,22 +117,22 @@ fn on_stack() -> Result<(), Infallible> {
111117
while let Some(elem) = buf.as_mut().pop() {
112118
panic!("found in empty buffer!: {elem}");
113119
}
114-
assert!(buf.as_mut().push(10)?);
115-
assert!(buf.as_mut().push(42)?);
120+
assert!(buf.as_mut().push(10));
121+
assert!(buf.as_mut().push(42));
116122
assert_eq!(buf.as_mut().pop(), Some(10));
117123
assert_eq!(buf.as_mut().pop(), Some(42));
118124
assert_eq!(buf.as_mut().pop(), None);
119-
assert!(buf.as_mut().push(42)?);
120-
assert!(buf.as_mut().push(24)?);
125+
assert!(buf.as_mut().push(42));
126+
assert!(buf.as_mut().push(24));
121127
assert_eq!(buf.as_mut().pop(), Some(42));
122-
assert!(buf.as_mut().push(25)?);
128+
assert!(buf.as_mut().push(25));
123129
assert_eq!(buf.as_mut().pop(), Some(24));
124130
assert_eq!(buf.as_mut().pop(), Some(25));
125131
assert_eq!(buf.as_mut().pop(), None);
126132
for i in 0..63 {
127-
assert!(buf.as_mut().push(i)?);
133+
assert!(buf.as_mut().push(i));
128134
}
129-
assert!(!buf.as_mut().push(42)?);
135+
assert!(!buf.as_mut().push(42));
130136
for i in 0..63 {
131137
if let Some(value) = buf.as_mut().pop_no_stack() {
132138
stack_pin_init!(let value = value);
@@ -199,13 +205,13 @@ fn even_failing() {
199205
#[test]
200206
fn with_failing_inner() {
201207
let mut buf = Box::pin_init(RingBuffer::<EvenU64, 4>::new()).unwrap();
202-
assert_eq!(buf.as_mut().push(EvenU64::new(0)), Ok(true));
203-
assert_eq!(buf.as_mut().push(EvenU64::new(1)), Err(()));
204-
assert_eq!(buf.as_mut().push(EvenU64::new(2)), Ok(true));
205-
assert_eq!(buf.as_mut().push(EvenU64::new(3)), Err(()));
206-
assert_eq!(buf.as_mut().push(EvenU64::new(4)), Ok(true));
207-
assert_eq!(buf.as_mut().push(EvenU64::new(5)), Ok(false));
208-
assert_eq!(buf.as_mut().push(EvenU64::new(6)), Ok(false));
208+
assert_eq!(buf.as_mut().try_push(EvenU64::new(0)), Ok(true));
209+
assert_eq!(buf.as_mut().try_push(EvenU64::new(1)), Err(()));
210+
assert_eq!(buf.as_mut().try_push(EvenU64::new(2)), Ok(true));
211+
assert_eq!(buf.as_mut().try_push(EvenU64::new(3)), Err(()));
212+
assert_eq!(buf.as_mut().try_push(EvenU64::new(4)), Ok(true));
213+
assert_eq!(buf.as_mut().try_push(EvenU64::new(5)), Ok(false));
214+
assert_eq!(buf.as_mut().try_push(EvenU64::new(6)), Ok(false));
209215

210216
assert_eq!(
211217
buf.as_mut().pop(),
@@ -259,15 +265,15 @@ fn with_big_struct() {
259265
let mut buf = buf.lock();
260266
for _ in 0..63 {
261267
assert_eq!(
262-
buf.as_mut().push(init!(BigStruct{
268+
buf.as_mut().try_push(init!(BigStruct{
263269
buf <- zeroed::<_, Infallible>(),
264270
oth <- uninit::<_, Infallible>(),
265271
})),
266272
Ok(true)
267273
);
268274
}
269275
assert_eq!(
270-
buf.as_mut().push(init!(BigStruct{
276+
buf.as_mut().try_push(init!(BigStruct{
271277
buf <- zeroed::<_, Infallible>(),
272278
oth <- uninit::<_, Infallible>(),
273279
})),

tests/ui/no_pin_data_but_pinned_drop.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,9 @@ error[E0277]: the trait bound `Foo: HasPinData` is not satisfied
55
| ^^^ the trait `HasPinData` is not implemented for `Foo`
66
|
77
note: required by a bound in `PinnedDrop`
8-
--> $SRC_DIR/src/lib.rs:1094:30
8+
--> $SRC_DIR/src/lib.rs:1234:30
99
|
10-
1094 | pub unsafe trait PinnedDrop: __internal::HasPinData {
10+
1234 | pub unsafe trait PinnedDrop: __internal::HasPinData {
1111
| ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `PinnedDrop`
1212

1313
error: aborting due to 1 previous error

0 commit comments

Comments
 (0)