Skip to content

Commit f884ca3

Browse files
Introduce out references, extra helper traits, and doc improvements
* Fixed unsoundness of using `&mut MaybeUninit<T>` as the out type for `&mut T` * Various improvements as suggested by @HeroicKatora It features: - enhancing `Vec::reserve_uninit` to a more general API for accessing with the backing buffer / capacity of a Vec; - Rename `idx` to `.get_out` - (Ab)use `.copy_from_slice()` in read implementations to use less unsafe Co-Authored-By: Andreas Molzer <[email protected]> * Improve docs, start implementing iteration logic * Unified `Out` and `OutSlice` into a single `Out<T : ?Sized> type` Not only does this lead to replacing `OutSlice<T>` with the more readable `Out<[T]>`, it also results in other parts of the API being greatly simplified (mainly the `AsOut` trait). * Improve code as per @HeroicKatora suggestions. Co-Authored-By: Andreas Molzer <[email protected]>
1 parent a42410f commit f884ca3

20 files changed

+2342
-786
lines changed

.cargo/config

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[build]
2+
rustdocflags = ["-C", "opt-level=2"]

Cargo.toml

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
[lib]
2+
crate-type = ["rlib"]
3+
14
[package]
25
name = "uninit"
36
version = "0.1.0"
@@ -18,12 +21,13 @@ license = "MIT"
1821
require_unsafe_in_body = "0.2.0"
1922

2023
[features]
21-
chain = []
22-
downcast_as_ReadIntoUninit = []
24+
std = []
2325
nightly = []
24-
polonius-check = ["nightly"]
26+
specialization = ["nightly"]
27+
const_generics = ["nightly"]
28+
chain = []
2529

26-
default = []
30+
default = ["std"]
2731

2832
[package.metadata.docs.rs]
2933
features = [ "nightly" ]

README.md

Lines changed: 40 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -51,15 +51,28 @@ Note that there are other ways to trigger this UB without explicitely using
5151
```
5252

5353
- this is exactly equivalent to calling `mem::uninitialized::<T>()`,
54+
which breaks the _validity_ invariant of `T` and thus causes
55+
"instant UB".
56+
57+
There currently only two exceptions / _valid_ use cases:
58+
59+
- either [`type T = [MaybeUninit<U>; N]`][`uninit_array`],
60+
61+
- or `T` is an inhabited ZST (this may, however, break safety
62+
invariants associated with the properties of the type, causing UB
63+
once such broken invariant is witnessed).
5464

5565
- yes, using [`MaybeUninit`] is more subtle than just changing a function
5666
call.
5767

5868
- ```rust
5969
let mut vec: Vec<u8> = Vec::with_capacity(100); // Fine
6070
unsafe {
61-
vec.set_len(100); // UB: we have an uninitialized [u8; 100] in the heap
71+
vec.set_len(100); // we have an uninitialized [u8; 100] in the heap
72+
// This has broken the _safety_ invariant of `Vec`, but is not yet UB
73+
// since no code has witnessed the broken state
6274
}
75+
let heap_bytes: &[u8] = &*vec; // Witness the broken safety invariant: UB!
6376
```
6477

6578
## Instead, (you can) use [`MaybeUninit`]
@@ -98,6 +111,7 @@ It is all about the _**delayed** initialization pattern_:
98111
let mut x = MaybeUninit::<i32>::uninit();
99112

100113
x = MaybeUninit::new(42);
114+
assert_eq!(42, unsafe { x.assume_init() });
101115
```
102116

103117
- or through a raw `*mut T` pointer (contrary to Rust references,
@@ -110,9 +124,24 @@ It is all about the _**delayed** initialization pattern_:
110124

111125
unsafe {
112126
x.as_mut_ptr().write(42);
127+
assert_eq!(x.assume_init(), 42);
113128
}
114129
```
115130

131+
- or, if you use the tools of this crate, by upgrading the
132+
`&mut MaybeUninit<T>` into a "`&out T`" type called
133+
[`Out<T>`][`crate::prelude::Out`]:
134+
135+
```rust
136+
#![forbid(unsafe_code)] // no unsafe!
137+
use ::core::mem::MaybeUninit;
138+
use ::uninit::prelude::*;
139+
140+
let mut x = MaybeUninit::uninit();
141+
let at_init_x: &i32 = x.as_out().write(42);
142+
assert_eq!(at_init_x, &42);
143+
```
144+
116145
3. **Type-level upgrade**
117146

118147
Once we know, for sure, that the memory has been initialized, we can
@@ -151,9 +180,9 @@ pub trait Read {
151180
}
152181
```
153182

154-
that is, there is no way to `.read()` into an unitialized buffer (it would
183+
that is, there is no way to `.read()` into an uninitialized buffer (it would
155184
require an api taking either a `(*mut u8, usize)` pair, or, equivalently and
156-
by the way more ergonomically, a `&mut [MaybeUninit<u8>]`).
185+
by the way more ergonomically, a [`&out [u8]`][`crate::prelude::Out`]).
157186

158187
# Enter `::uninit`
159188

@@ -163,7 +192,7 @@ So, the objective of this crate is double:
163192

164193
For instance:
165194

166-
- [`uninit_byte_array!`]
195+
- [`uninit_array!`]
167196

168197
- [`Vec::reserve_uninit`]
169198

@@ -174,19 +203,14 @@ So, the objective of this crate is double:
174203

175204
- [`ReadIntoUninit`]
176205

177-
- [`.init_with_copy_from_slice()`]
178-
179-
## Status
180-
181-
This is currently at an realy stage, so it "only" includes
182-
utilities to work with **uninitialized bytes** or integers.
206+
- [Initialize an uninitialized buffer with `.copy_from_slice()`]
183207

184208
[`Read`]: https://doc.rust-lang.org/1.36.0/std/io/trait.Read.html
185209
[`mem::uninitialized`]: https://doc.rust-lang.org/core/mem/fn.uninitialized.html
186210
[`MaybeUninit`]: https://doc.rust-lang.org/core/mem/union.MaybeUninit.html
187-
[`.assume_init_by_ref()`]: https://docs.rs/uninit/0.1.0/uninit/trait.MaybeUninitExt.html#method.assume_init_by_ref
188-
[`.assume_init_by_mut()`]: https://docs.rs/uninit/0.1.0/uninit/trait.MaybeUninitExt.html#method.assume_init_by_mut
189-
[`uninit_byte_array!`]: https://docs.rs/uninit/0.1.0/uninit/macro.uninit_byte_array.html
190-
[`Vec::reserve_uninit`]: https://docs.rs/uninit/0.1.0/uninit/trait.VecReserveUninit.html#tymethod.reserve_uninit
191-
[`.init_with_copy_from_slice()`]: https://docs.rs/uninit/0.1.0/uninit/trait.InitWithCopyFromSlice.html#tymethod.init_with_copy_from_slice
192-
[`ReadIntoUninit`]: https://docs.rs/uninit/0.1.0/uninit/trait.ReadIntoUninit.html
211+
[`.assume_init_by_ref()`]: https://docs.rs/uninit/0.2.0/uninit/extension_traits/trait.MaybeUninitExt.html#tymethod.assume_init_by_ref
212+
[`.assume_init_by_mut()`]: https://docs.rs/uninit/0.2.0/uninit/extension_traits/trait.MaybeUninitExt.html#tymethod.assume_init_by_mut
213+
[`uninit_array!`]: https://docs.rs/uninit/0.2.0/uninit/macro.uninit_byte_array.html
214+
[`Vec::reserve_uninit`]: https://docs.rs/uninit/0.2.0/uninit/extension_traits/trait.VecCapacity.html#tymethod.reserve_uninit
215+
[Initialize an uninitialized buffer with `.copy_from_slice()`]: https://docs.rs/uninit/0.2.0/uninit/out_ref/struct.Out.html#method.copy_from_slice
216+
[`ReadIntoUninit`]: https://docs.rs/uninit/0.2.0/uninit/read/trait.ReadIntoUninit.html

src/extension_traits/as_out.rs

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
use_prelude!();
2+
3+
use ::core::mem::ManuallyDrop;
4+
5+
#[cfg(doc)]
6+
use crate::extension_traits::ManuallyDropMut;
7+
8+
/// Extension trait to convert a `&mut _` into a `&out ` by calling
9+
/// `.as_out()` on it.
10+
///
11+
/// By autoref, this means that you can even just extract a `&out T` reference
12+
/// out of a `mut` element simply by calling `.as_out()` on it.
13+
///
14+
/// There is, however, one restriction: to be able to call `.as_out()` on
15+
/// something, it needs to be either `Copy`, or a value wrapped in a
16+
/// [`MaybeUninit`] / a [`ManuallyDrop`].
17+
///
18+
/// This is by design. Indeed, [`Out`] references do not call the destructor
19+
/// of the overwritten element (since it may not be initialized).
20+
/// This could cause memory leaks when there is an initialized element with
21+
/// [drop glue][`core::mem::needs_drop`].
22+
///
23+
/// To solve this limitation, one must explicitly call
24+
/// [`.manually_drop_mut()`][`ManuallyDropMut::manually_drop_mut`]
25+
/// to automagically transmute the `&mut _` reference into a
26+
/// `&mut ManuallyDrop<_>`.
27+
///
28+
/// # Examples
29+
///
30+
/// ```rust
31+
/// use ::uninit::prelude::*;
32+
///
33+
/// let mut x = 0;
34+
/// x.as_out().write(42);
35+
///
36+
/// let mut y = ::core::mem::MaybeUninit::uninit();
37+
/// y.as_out().write(42);
38+
/// let y = unsafe { y.assume_init() };
39+
///
40+
/// assert_eq!(x, y);
41+
/// ```
42+
pub
43+
trait AsOut<Pointee : ?Sized> {
44+
#[allow(missing_docs)]
45+
fn as_out<'out> (self: &'out mut Self)
46+
-> Out<'out, Pointee>
47+
;
48+
}
49+
50+
impl<T> AsOut<T> for MaybeUninit<T> {
51+
#[inline]
52+
fn as_out<'out> (self: &'out mut MaybeUninit<T>)
53+
-> Out<'out, T>
54+
{
55+
self.into()
56+
}
57+
}
58+
59+
impl<T> AsOut<T> for T
60+
where
61+
T : Copy,
62+
{
63+
#[inline]
64+
fn as_out<'out> (self: &'out mut T)
65+
-> Out<'out, T>
66+
{
67+
self.into()
68+
}
69+
}
70+
71+
impl<T> AsOut<[T]> for [MaybeUninit<T>] {
72+
#[inline]
73+
fn as_out<'out> (self: &'out mut [MaybeUninit<T>])
74+
-> Out<'out, [T]>
75+
{
76+
self.into()
77+
}
78+
}
79+
80+
impl<T> AsOut<[T]> for [T]
81+
where
82+
T : Copy,
83+
{
84+
#[inline]
85+
fn as_out<'out> (self: &'out mut [T])
86+
-> Out<'out, [T]>
87+
{
88+
self.into()
89+
}
90+
}
91+
92+
impl<T> AsOut<T> for ManuallyDrop<T> {
93+
#[inline]
94+
fn as_out<'out> (self: &'out mut ManuallyDrop<T>)
95+
-> Out<'out, T>
96+
{
97+
self.into()
98+
}
99+
}
100+
101+
impl<T> AsOut<[T]> for [ManuallyDrop<T>] {
102+
#[inline]
103+
fn as_out<'out> (self: &'out mut [ManuallyDrop<T>])
104+
-> Out<'out, [T]>
105+
{
106+
self.into()
107+
}
108+
}
109+
110+
#[cfg(not(feature = "const_generics"))]
111+
const _: () = {
112+
macro_rules! impl_arrays {( $($N:tt)* ) => ($(
113+
impl<T> AsOut<[T]> for [MaybeUninit<T>; $N] {
114+
#[inline]
115+
fn as_out<'out> (self: &'out mut Self)
116+
-> Out<'out, [T]>
117+
{
118+
From::from(&mut self[..])
119+
}
120+
}
121+
impl<T> AsOut<[T]> for [T; $N]
122+
where
123+
T : Copy,
124+
{
125+
#[inline]
126+
fn as_out<'out> (self: &'out mut Self)
127+
-> Out<'out, [T]>
128+
{
129+
From::from(&mut self[..])
130+
}
131+
}
132+
)*)}
133+
134+
impl_arrays! {
135+
0 1 2 3 4 5 6 7
136+
8 9 10 11 12 13 14 15
137+
16 17 18 19 20 21 22 23
138+
24 25 26 27 28 29 30 31
139+
32 33 34 35 36 37 38 39
140+
40 41 42 43 44 45 46 47
141+
48 49 50 51 52 53 54 55
142+
56 57 58 59 60 61 62 63
143+
64
144+
128
145+
256
146+
512
147+
1024
148+
}
149+
};
150+
151+
#[cfg(feature = "const_generics")]
152+
const _: () = {
153+
#[doc(cfg(feature = "const_generics"))]
154+
impl<T, const N: usize> AsOut<[T]> for [MaybeUninit<T>; N] {
155+
#[inline]
156+
fn as_out<'out> (self: &'out mut Self)
157+
-> Out<'out, [T]>
158+
{
159+
From::from(&mut self[..])
160+
}
161+
}
162+
#[doc(cfg(feature = "const_generics"))]
163+
impl<T, const N: usize> AsOut<[T]> for [T; N]
164+
where
165+
T : Copy,
166+
{
167+
#[inline]
168+
fn as_out<'out> (self: &'out mut Self)
169+
-> Out<'out, [T]>
170+
{
171+
From::from(&mut self[..])
172+
}
173+
}
174+
};

0 commit comments

Comments
 (0)