Skip to content

Commit

Permalink
Add functions to initialize arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
y86-dev committed May 5, 2023
1 parent 1f506ed commit a1cc1c3
Show file tree
Hide file tree
Showing 2 changed files with 77 additions and 2 deletions.
75 changes: 75 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1414,6 +1414,81 @@ pub fn uninit<T, E>() -> impl Init<MaybeUninit<T>, E> {
unsafe { init_from_closure(|_| Ok(())) }
}

/// Initializes an array by initializing each element via the provided initializer.
pub fn init_array_from_fn<I, const N: usize, T, E>(
mut make_init: impl FnMut(usize) -> I,
) -> impl Init<[T; N], E>
where
I: Init<T, E>,
{
let init = move |slot: *mut [T; N]| {
let slot = slot.cast::<T>();
for i in 0..N {
let init = make_init(i);
// SAFETY: since 0 <= `i` < N, it is still in bounds of `[T; N]`.
let ptr = unsafe { slot.add(i) };
// SAFETY: The pointer is derived from `slot` and thus satisfies the `__init`
// requirements.
match unsafe { init.__init(ptr) } {
Ok(()) => {}
Err(e) => {
// We now free every element that has been initialized before:
for j in 0..i {
let ptr = unsafe { slot.add(j) };
// SAFETY: The value was initialized in a previous iteration of the loop
// and since we return `Err` below, the caller will consider the memory at
// `slot` as uninitialized.
unsafe { ptr::drop_in_place(ptr) };
}
return Err(e);
}
}
}
Ok(())
};
// SAFETY: The initializer above initializes every element of the array. On failure it drops
// any initialized elements and returns `Err`.
unsafe { init_from_closure(init) }
}

/// Initializes an array by initializing each element via the provided initializer.
pub fn pin_init_array_from_fn<I, const N: usize, T, E>(
mut make_init: impl FnMut(usize) -> I,
) -> impl PinInit<[T; N], E>
where
I: PinInit<T, E>,
{
let init = move |slot: *mut [T; N]| {
let slot = slot.cast::<T>();
for i in 0..N {
let init = make_init(i);
// SAFETY: since 0 <= `i` < N, it is still in bounds of `[T; N]`.
let ptr = unsafe { slot.offset(i.try_into().unwrap()) };
// SAFETY: The pointer is derived from `slot` and thus satisfies the `__pinned_init`
// requirements.
match unsafe { init.__pinned_init(ptr) } {
Ok(()) => {}
Err(e) => {
// We now have to free every element that has been initialized before, since we
// have to abide by the drop guarantee.
for j in 0..i {
let ptr = unsafe { slot.add(j) };
// SAFETY: The value was initialized in a previous iteration of the loop
// and since we return `Err` below, the caller will consider the memory at
// `slot` as uninitialized.
unsafe { ptr::drop_in_place(ptr) };
}
return Err(e);
}
}
}
Ok(())
};
// SAFETY: The initializer above initializes every element of the array. On failure it drops
// any initialized elements and returns `Err`.
unsafe { pin_init_from_closure(init) }
}

// SAFETY: Every type can be initialized by-value.
unsafe impl<T> Init<T> for T {
unsafe fn __init(self, slot: *mut T) -> Result<(), Infallible> {
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/no_pin_data_but_pinned_drop.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ error[E0277]: the trait bound `Foo: HasPinData` is not satisfied
| ^^^ the trait `HasPinData` is not implemented for `Foo`
|
note: required by a bound in `PinnedDrop`
--> $SRC_DIR/src/lib.rs:1547:30
--> $SRC_DIR/src/lib.rs:1626:30
|
1547 | pub unsafe trait PinnedDrop: __internal::HasPinData {
1626 | pub unsafe trait PinnedDrop: __internal::HasPinData {
| ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `PinnedDrop`

error: aborting due to previous error
Expand Down

0 comments on commit a1cc1c3

Please sign in to comment.