Skip to content

Commit e3b2cb6

Browse files
committed
refactor: implement [Pin]Init for ArrayInit
Renames `ArrayInitGuard` to `ArrayInit`. Implements `[Pin]Init` for it so that it can be used directly in `[pin_]init_array_from_fn` instead of having to wrap it in a closure. Suggested-by: Gary Guo <gary@garyguo.net> Signed-off-by: Mirko Adzic <adzicmirko97@gmail.com>
1 parent 225f13b commit e3b2cb6

2 files changed

Lines changed: 76 additions & 66 deletions

File tree

src/__internal.rs

Lines changed: 56 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -282,66 +282,76 @@ impl<T: ?Sized> Drop for DropGuard<T> {
282282
///
283283
/// Drops the already initialized elements of the array if an error or panic occurs
284284
/// partway through the initialization process.
285-
pub struct ArrayInitGuard<T> {
286-
/// A pointer to the first element of the array.
285+
pub struct ArrayInit<T, F> {
286+
/// A pointer to the first element of the array. Null until `__init` or `__pinned_init`
287+
/// is called.
287288
ptr: *mut T,
288-
/// The length of the array.
289-
len: usize,
290289
/// The number of initialized elements in the array.
291290
num_init: usize,
291+
/// Initialization function factory.
292+
make_init: F,
292293
}
293294

294-
impl<T> ArrayInitGuard<T> {
295-
/// Creates a new [`ArrayInitGuard<T>`] for the array starting at `ptr` with length `len`.
296-
///
295+
impl<T, F> ArrayInit<T, F> {
297296
/// # Safety
298297
///
299-
/// `ptr` must be a valid pointer to the first element of an array of length `len`. The
300-
/// memory must be uninitialized, and it is the caller's responsibility to ensure that:
301-
/// - the elements of the array will only be initialized through this guard,
302-
/// - the elements of the array will not be accessed by any other means until they
303-
/// are initialized, and
304-
/// - the elements of the array will not be dropped by any other means for the entire
305-
/// lifetime of this guard.
306-
pub unsafe fn new(ptr: *mut T, len: usize) -> Self {
298+
/// This function may only be called from
299+
/// [`init_array_from_fn`](crate::init_array_from_fn) or
300+
/// [`pin_init_array_from_fn`](crate::pin_init_array_from_fn).
301+
pub(crate) unsafe fn new(make_init: F) -> Self {
307302
Self {
308-
ptr,
309-
len,
303+
ptr: core::ptr::null_mut(),
310304
num_init: 0,
305+
make_init,
311306
}
312307
}
308+
}
313309

314-
/// Initializes the array using the provided closure.
315-
pub fn init<I, E>(mut self, mut make_init: impl FnMut(usize) -> I) -> Result<(), E>
316-
where
317-
I: Init<T, E>,
318-
{
319-
for i in 0..self.len {
320-
let init = make_init(i);
321-
// SAFETY: Since `0 <= i < self.len`, `self.ptr.add(i)` is in bounds and valid for
322-
// writes by the safety contract of `new`.
310+
/// SAFETY: On success, all `N` elements of the array have been initialized through
311+
/// `I: Init`. On error or panic, the elements that have been initialized so far are
312+
/// dropped, thus leaving the array uninitialized and ready to deallocate. The `Init`
313+
/// implementation executes the same code as that of `PinInit`.
314+
unsafe impl<T, F, I, E, const N: usize> Init<[T; N], E> for ArrayInit<T, F>
315+
where
316+
F: FnMut(usize) -> I,
317+
I: Init<T, E>,
318+
{
319+
unsafe fn __init(mut self, slot: *mut [T; N]) -> Result<(), E> {
320+
self.ptr = slot.cast::<T>();
321+
for i in 0..N {
322+
let init = (self.make_init)(i);
323+
// SAFETY: Since `0 <= i < N`, `self.ptr.add(i)` is in bounds and
324+
// valid for writes by the safety contract of `__init`.
323325
let ptr = unsafe { self.ptr.add(i) };
324-
// SAFETY: The pointer is derived from `self.ptr` and thus satisfies the `__init`
325-
// requirements.
326+
// SAFETY: The pointer is derived from `slot` and thus satisfies the
327+
// `__init` requirements.
326328
unsafe { init.__init(ptr) }?;
327329
self.num_init += 1;
328330
}
329331
core::mem::forget(self);
330332
Ok(())
331333
}
334+
}
332335

333-
/// Initializes the array using the provided closure, which is allowed to pin the elements.
334-
pub fn pin_init<I, E>(mut self, mut make_init: impl FnMut(usize) -> I) -> Result<(), E>
335-
where
336-
I: PinInit<T, E>,
337-
{
338-
for i in 0..self.len {
339-
let init = make_init(i);
340-
// SAFETY: Since `0 <= i < self.len`, `self.ptr.add(i)` is in bounds and valid for
341-
// writes by the safety contract of `new`.
336+
/// SAFETY: On success, all `N` elements of the array have been initialized through
337+
/// `I`. Since `I: PinInit` guarantees that the pinning invariants of `T` are upheld,
338+
/// the guarantees of `[T; N]` are also upheld. On error or panic, the elements that
339+
/// have been initialized so far are dropped, thus leaving the array uninitialized
340+
/// and ready to deallocate.
341+
unsafe impl<T, F, I, E, const N: usize> PinInit<[T; N], E> for ArrayInit<T, F>
342+
where
343+
F: FnMut(usize) -> I,
344+
I: PinInit<T, E>,
345+
{
346+
unsafe fn __pinned_init(mut self, slot: *mut [T; N]) -> Result<(), E> {
347+
self.ptr = slot.cast::<T>();
348+
for i in 0..N {
349+
let init = (self.make_init)(i);
350+
// SAFETY: Since `0 <= i < N`, `self.ptr.add(i)` is in bounds and
351+
// valid for writes by the safety contract of `__pinned_init`.
342352
let ptr = unsafe { self.ptr.add(i) };
343-
// SAFETY: The pointer is derived from `self.ptr` and thus satisfies the `__pinned_init`
344-
// requirements.
353+
// SAFETY: The pointer is derived from `slot` and thus satisfies the
354+
// `__pinned_init` requirements.
345355
unsafe { init.__pinned_init(ptr) }?;
346356
self.num_init += 1;
347357
}
@@ -350,9 +360,14 @@ impl<T> ArrayInitGuard<T> {
350360
}
351361
}
352362

353-
impl<T> Drop for ArrayInitGuard<T> {
363+
impl<T, F> Drop for ArrayInit<T, F> {
354364
fn drop(&mut self) {
355-
// SAFETY: safety contract of `ArrayInitGuard` guarantees that elements
365+
if self.ptr.is_null() {
366+
// No initialization had been attempted, nothing to drop.
367+
return;
368+
}
369+
370+
// SAFETY: Safety contract of `ArrayInit` guarantees that elements
356371
// `self.ptr[0..self.num_init]` are initialized and contain valid `T`
357372
// values, so dropping them is safe.
358373
unsafe {

src/lib.rs

Lines changed: 20 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1202,18 +1202,16 @@ pub fn init_array_from_fn<I, const N: usize, T, E>(
12021202
where
12031203
I: Init<T, E>,
12041204
{
1205-
let init = move |slot: *mut [T; N]| {
1206-
let ptr = slot.cast::<T>();
1207-
// SAFETY: per `__init` safety requirements:
1208-
// - `ptr` points to valid uninitialized memory (`[T; N]`),
1209-
// - it does not move for the duration of initialization,
1210-
// - this closure has exclusive access to initialize it.
1211-
let guard = unsafe { __internal::ArrayInitGuard::new(ptr, N) };
1212-
guard.init(make_init)
1213-
};
1214-
// SAFETY: The initializer above initializes every element of the array. On failure it drops
1215-
// any initialized elements and returns `Err` or propagates the panic.
1216-
unsafe { init_from_closure(init) }
1205+
// SAFETY: `ArrayInit` initializes every element of the array. On failure it
1206+
// drops any initialized elements and returns `Err` or propagates the panic.
1207+
//
1208+
// Per `__init` safety requirements:
1209+
// - `slot` must be a valid pointer to uninitialized `[T; N]`,
1210+
// - the elements of the array must not be accessed by any other means until
1211+
// they are initialized,
1212+
// - the elements of the array must not be dropped by any other means for the
1213+
// entire lifetime of `ArrayInit`.
1214+
unsafe { __internal::ArrayInit::new(make_init) }
12171215
}
12181216

12191217
/// Initializes an array by initializing each element via the provided initializer.
@@ -1237,19 +1235,16 @@ pub fn pin_init_array_from_fn<I, const N: usize, T, E>(
12371235
where
12381236
I: PinInit<T, E>,
12391237
{
1240-
let init = move |slot: *mut [T; N]| {
1241-
let ptr = slot.cast::<T>();
1242-
// SAFETY: per `__pinned_init` safety requirements:
1243-
// - `ptr` points to valid uninitialized memory (`[T; N]),
1244-
// - it does not move for the duration of initialization,
1245-
// - this closure has exclusive access to initialize it,
1246-
// - the pinning invariants of `T` are upheld while initializing.
1247-
let guard = unsafe { __internal::ArrayInitGuard::new(ptr, N) };
1248-
guard.pin_init(make_init)
1249-
};
1250-
// SAFETY: The initializer above initializes every element of the array. On failure it drops
1251-
// any initialized elements and returns `Err` or propagates the panic.
1252-
unsafe { pin_init_from_closure(init) }
1238+
// SAFETY: `ArrayInit` initializes every element of the array. On failure it
1239+
// drops any initialized elements and returns `Err` or propagates the panic.
1240+
//
1241+
// Per `__pinned_init` safety requirements:
1242+
// - `slot` must be a valid pointer to uninitialized `[T; N]`,
1243+
// - the elements of the array must not be accessed by any other means until
1244+
// they are initialized,
1245+
// - the elements of the array must not be dropped by any other means for the
1246+
// entire lifetime of `ArrayInit`.
1247+
unsafe { __internal::ArrayInit::new(make_init) }
12531248
}
12541249

12551250
/// Construct an initializer in a closure and run it.

0 commit comments

Comments
 (0)