Skip to content

Commit 2afe341

Browse files
bors[bot]CAD97
andauthored
Merge #54
54: Add a fallible analogue to `AllocSliceDst` r=CAD97 a=CAD97 bors: r+ Co-authored-by: CAD97 <cad97@cad97.com>
2 parents db464a5 + 04b4f83 commit 2afe341

File tree

4 files changed

+96
-16
lines changed

4 files changed

+96
-16
lines changed

Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/slice-dst/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "slice-dst"
3-
version = "1.3.0"
3+
version = "1.4.0"
44
edition = "2018"
55

66
authors = ["Christopher Durham (cad97) <cad97@cad97.com>"]

crates/slice-dst/README.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ in exchange for moving node payloads to the heap alongside the children array.
7575

7676
## Changelist
7777

78+
### 1.4.0
79+
#### Additions
80+
81+
- Added the `TryAllocSliceDst`, a fallible analogue to `AllocSliceDst`.
82+
7883
### 1.3.0
7984
#### Additions
8085

crates/slice-dst/src/lib.rs

Lines changed: 89 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -181,24 +181,95 @@ where
181181
}
182182

183183
/// Types that can allocate a custom slice DST within them.
184+
///
185+
/// # Implementation note
186+
///
187+
/// For most types, [`TryAllocSliceDst`] should be the implementation primitive.
188+
/// This trait can then be implemented in terms of `TryAllocSliceDst`:
189+
///
190+
/// ```rust
191+
/// # use {slice_dst::*, std::ptr};
192+
/// # struct Container<T: ?Sized>(Box<T>);
193+
/// # unsafe impl<S: ?Sized + SliceDst> TryAllocSliceDst<S> for Container<S> {
194+
/// # unsafe fn try_new_slice_dst<I, E>(len: usize, init: I) -> Result<Self, E>
195+
/// # where I: FnOnce(ptr::NonNull<S>) -> Result<(), E>
196+
/// # { unimplemented!() }
197+
/// # }
198+
/// unsafe impl<S: ?Sized + SliceDst> AllocSliceDst<S> for Container<S> {
199+
/// unsafe fn new_slice_dst<I>(len: usize, init: I) -> Self
200+
/// where
201+
/// I: FnOnce(ptr::NonNull<S>),
202+
/// {
203+
/// enum Void {} // or never (!) once it is stable
204+
/// #[allow(clippy::unit_arg)]
205+
/// let init = |ptr| Ok::<(), Void>(init(ptr));
206+
/// match Self::try_new_slice_dst(len, init) {
207+
/// Ok(a) => a,
208+
/// Err(void) => match void {},
209+
/// }
210+
/// }
211+
/// }
212+
/// ```
213+
///
214+
/// This is not a blanket impl due to coherence rules; if the blanket impl were present,
215+
/// it would be impossible to implement `AllocSliceDst` instead of `TryAllocSliceDst`.
184216
pub unsafe trait AllocSliceDst<S: ?Sized + SliceDst> {
185217
/// Create a new custom slice DST.
186218
///
187219
/// # Safety
188220
///
189221
/// `init` must properly initialize the object behind the pointer.
190-
/// The stored length of the slice DST must be the same as the length used in this call.
191222
/// `init` receives a fully uninitialized pointer and must not read anything before writing.
192223
unsafe fn new_slice_dst<I>(len: usize, init: I) -> Self
193224
where
194225
I: FnOnce(ptr::NonNull<S>);
195226
}
196227

228+
// FUTURE: export? Would need better generic support.
229+
macro_rules! impl_alloc_by_try_alloc {
230+
($T:ident) => {
231+
unsafe impl<S: ?Sized + SliceDst> $crate::AllocSliceDst<S> for $T<S> {
232+
unsafe fn new_slice_dst<I>(len: usize, init: I) -> Self
233+
where
234+
I: FnOnce(::core::ptr::NonNull<S>),
235+
{
236+
enum Void {}
237+
#[allow(clippy::unit_arg)]
238+
let init = |ptr| ::core::result::Result::<(), Void>::Ok(init(ptr));
239+
match <Self as $crate::TryAllocSliceDst<S>>::try_new_slice_dst(len, init) {
240+
Ok(a) => a,
241+
Err(void) => match void {},
242+
}
243+
}
244+
}
245+
};
246+
}
247+
248+
/// Types that can allocate a custom slice DST within them,
249+
/// given a fallible initialization function.
250+
pub unsafe trait TryAllocSliceDst<S: ?Sized + SliceDst>: AllocSliceDst<S> + Sized {
251+
/// Create a new custom slice DST with a fallible initialization function.
252+
///
253+
/// # Safety
254+
///
255+
/// `init` must properly initialize the object behind the pointer.
256+
/// `init` receives a fully uninitialized pointer and must not read anything before writing.
257+
///
258+
/// If the initialization closure panics or returns an error,
259+
/// the allocated place will be deallocated but not dropped.
260+
/// To clean up the partially initialized type, we suggest
261+
/// proxying creation through scope guarding types.
262+
unsafe fn try_new_slice_dst<I, E>(len: usize, init: I) -> Result<Self, E>
263+
where
264+
I: FnOnce(ptr::NonNull<S>) -> Result<(), E>;
265+
}
266+
197267
// SAFETY: Box is guaranteed to be allocatable by GlobalAlloc.
198-
unsafe impl<S: ?Sized + SliceDst> AllocSliceDst<S> for Box<S> {
199-
unsafe fn new_slice_dst<I>(len: usize, init: I) -> Self
268+
impl_alloc_by_try_alloc!(Box);
269+
unsafe impl<S: ?Sized + SliceDst> TryAllocSliceDst<S> for Box<S> {
270+
unsafe fn try_new_slice_dst<I, E>(len: usize, init: I) -> Result<Self, E>
200271
where
201-
I: FnOnce(ptr::NonNull<S>),
272+
I: FnOnce(ptr::NonNull<S>) -> Result<(), E>,
202273
{
203274
struct RawBox<S: ?Sized + SliceDst>(ptr::NonNull<S>, Layout);
204275

@@ -223,26 +294,30 @@ unsafe impl<S: ?Sized + SliceDst> AllocSliceDst<S> for Box<S> {
223294
}
224295

225296
let ptr = RawBox::new(len);
226-
init(ptr.0);
227-
ptr.finalize()
297+
init(ptr.0)?;
298+
Ok(ptr.finalize())
228299
}
229300
}
230301

231-
unsafe impl<S: ?Sized + SliceDst> AllocSliceDst<S> for Rc<S> {
232-
unsafe fn new_slice_dst<I>(len: usize, init: I) -> Self
302+
// SAFETY: just delegates to `Box`'s implementation (for now?)
303+
impl_alloc_by_try_alloc!(Rc);
304+
unsafe impl<S: ?Sized + SliceDst> TryAllocSliceDst<S> for Rc<S> {
305+
unsafe fn try_new_slice_dst<I, E>(len: usize, init: I) -> Result<Self, E>
233306
where
234-
I: FnOnce(ptr::NonNull<S>),
307+
I: FnOnce(ptr::NonNull<S>) -> Result<(), E>,
235308
{
236-
Box::new_slice_dst(len, init).into()
309+
Box::try_new_slice_dst(len, init).map(Into::into)
237310
}
238311
}
239312

240-
unsafe impl<S: ?Sized + SliceDst> AllocSliceDst<S> for Arc<S> {
241-
unsafe fn new_slice_dst<I>(len: usize, init: I) -> Self
313+
// SAFETY: just delegates to `Box`'s implementation (for now?)
314+
impl_alloc_by_try_alloc!(Arc);
315+
unsafe impl<S: ?Sized + SliceDst> TryAllocSliceDst<S> for Arc<S> {
316+
unsafe fn try_new_slice_dst<I, E>(len: usize, init: I) -> Result<Self, E>
242317
where
243-
I: FnOnce(ptr::NonNull<S>),
318+
I: FnOnce(ptr::NonNull<S>) -> Result<(), E>,
244319
{
245-
Box::new_slice_dst(len, init).into()
320+
Box::try_new_slice_dst(len, init).map(Into::into)
246321
}
247322
}
248323

0 commit comments

Comments
 (0)