Skip to content

Commit 7b1c67f

Browse files
SimonSapinmaciejhirsz
authored andcommitted
Use a union to avoid UB with uninitialized &mut T (#6)
1 parent cdbc239 commit 7b1c67f

File tree

2 files changed

+22
-17
lines changed

2 files changed

+22
-17
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "toolshed"
3-
version = "0.6.3"
3+
version = "0.7.0"
44
authors = ["maciejhirsz <[email protected]>"]
55
license = "MIT/Apache-2.0"
66
description = "Arena allocator and a handful of useful data structures"

src/arena.rs

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,51 +23,56 @@ pub struct Arena {
2323
}
2424

2525
/// A pointer to an uninitialized region of memory.
26-
pub struct Uninitialized<'arena, T: 'arena> {
27-
pointer: &'arena mut T,
26+
pub struct Uninitialized<'arena, T: Copy + 'arena> {
27+
pointer: &'arena mut MaybeUninit<T>,
2828
}
2929

30-
impl<'arena, T: 'arena> Uninitialized<'arena, T> {
30+
/// Almost a copy of https://github.com/rust-lang/rust/issues/53491
31+
union MaybeUninit<T: Copy> {
32+
value: T,
33+
_uninit: (),
34+
}
35+
36+
impl<'arena, T: Copy + 'arena> Uninitialized<'arena, T> {
3137
/// Initialize the memory at the pointer with a given value.
3238
#[inline]
3339
pub fn init(self, value: T) -> &'arena mut T {
34-
*self.pointer = value;
35-
36-
self.pointer
40+
unsafe {
41+
self.pointer.value = value;
42+
&mut self.pointer.value
43+
}
3744
}
3845

3946
/// Get a reference to the pointer without writing to it.
4047
///
41-
/// **Reading from this reference without calling `init` is undefined behavior.**
48+
/// **Calling this method without calling `init` is undefined behavior.**
4249
#[inline]
4350
pub unsafe fn as_ref(&self) -> &'arena T {
44-
&*(self.pointer as *const T)
51+
&*(&self.pointer.value as *const T)
4552
}
4653

4754
/// Convert the `Uninitialized` to a regular mutable reference.
4855
///
49-
/// **Reading from this reference without calling `init` is undefined behavior.**
56+
/// **Calling this method without calling `init` is undefined behavior.**
5057
#[inline]
5158
pub unsafe fn as_mut_ref(self) -> &'arena mut T {
52-
self.pointer
59+
&mut self.pointer.value
5360
}
5461

5562
/// Convert a raw pointer to an `Uninitialized`. This method is unsafe since it can
5663
/// bind to arbitrary lifetimes.
5764
#[inline]
5865
pub unsafe fn from_raw(pointer: *mut T) -> Self {
5966
Uninitialized {
60-
pointer: &mut *pointer,
67+
pointer: &mut *(pointer as *mut MaybeUninit<T>),
6168
}
6269
}
6370
}
6471

65-
impl<'arena, T: 'arena> From<&'arena mut T> for Uninitialized<'arena, T> {
72+
impl<'arena, T: Copy + 'arena> From<&'arena mut T> for Uninitialized<'arena, T> {
6673
#[inline]
6774
fn from(pointer: &'arena mut T) -> Self {
68-
Uninitialized {
69-
pointer
70-
}
75+
unsafe { Self::from_raw(pointer) }
7176
}
7277
}
7378

@@ -167,7 +172,7 @@ impl Arena {
167172
#[inline]
168173
pub fn alloc_uninitialized<'arena, T: Sized + Copy>(&'arena self) -> Uninitialized<'arena, T> {
169174
Uninitialized {
170-
pointer: unsafe { &mut *(self.require(size_of::<T>()) as *mut T) }
175+
pointer: unsafe { &mut *(self.require(size_of::<T>()) as *mut MaybeUninit<T>) },
171176
}
172177
}
173178

0 commit comments

Comments
 (0)