Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 0d8b555

Browse files
committedOct 31, 2023
Implement MaybeUninit::fill{,_cloned,_mut,_with,_from}
ACP: rust-lang/libs-team#156
1 parent a395214 commit 0d8b555

File tree

3 files changed

+426
-24
lines changed

3 files changed

+426
-24
lines changed
 

‎library/core/src/mem/maybe_uninit.rs

Lines changed: 213 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1123,22 +1123,6 @@ impl<T> MaybeUninit<T> {
11231123
// unlike copy_from_slice this does not call clone_from_slice on the slice
11241124
// this is because `MaybeUninit<T: Clone>` does not implement Clone.
11251125

1126-
struct Guard<'a, T> {
1127-
slice: &'a mut [MaybeUninit<T>],
1128-
initialized: usize,
1129-
}
1130-
1131-
impl<'a, T> Drop for Guard<'a, T> {
1132-
fn drop(&mut self) {
1133-
let initialized_part = &mut self.slice[..self.initialized];
1134-
// SAFETY: this raw slice will contain only initialized objects
1135-
// that's why, it is allowed to drop it.
1136-
unsafe {
1137-
crate::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(initialized_part));
1138-
}
1139-
}
1140-
}
1141-
11421126
assert_eq!(this.len(), src.len(), "destination and source slices have different lengths");
11431127
// NOTE: We need to explicitly slice them to the same length
11441128
// for bounds checking to be elided, and the optimizer will
@@ -1147,7 +1131,7 @@ impl<T> MaybeUninit<T> {
11471131
let src = &src[..len];
11481132

11491133
// guard is needed b/c panic might happen during a clone
1150-
let mut guard = Guard { slice: this, initialized: 0 };
1134+
let mut guard = CloneGuard { slice: this, initialized: 0 };
11511135

11521136
for i in 0..len {
11531137
guard.slice[i].write(src[i].clone());
@@ -1160,6 +1144,202 @@ impl<T> MaybeUninit<T> {
11601144
unsafe { MaybeUninit::slice_assume_init_mut(this) }
11611145
}
11621146

1147+
/// Fills `this` with elements by copying `value`, returning a reference to
1148+
/// the now initialized contents of `this`.
1149+
///
1150+
/// This is similar to [`slice::fill`] but is restricted to `Copy` values.
1151+
/// Use [`MaybeUninit::fill_cloned`] to initialize from a `Clone` value.
1152+
///
1153+
/// # Examples
1154+
///
1155+
/// ```
1156+
/// #![feature(maybe_uninit_fill)]
1157+
/// use std::mem::MaybeUninit;
1158+
///
1159+
/// let mut dst = [MaybeUninit::uninit(); 5];
1160+
/// let init = MaybeUninit::fill(&mut dst, 0u8);
1161+
///
1162+
/// assert_eq!(init, &[0, 0, 0, 0, 0]);
1163+
/// ```
1164+
#[doc(alias = "memset")]
1165+
#[unstable(feature = "maybe_uninit_fill", issue = "117428")]
1166+
pub fn fill<'a>(this: &'a mut [MaybeUninit<T>], value: T) -> &'a [T]
1167+
where
1168+
T: Copy,
1169+
{
1170+
this.fill(MaybeUninit::new(value));
1171+
// SAFETY: Valid elements have just been copied into `this` so it is initialized
1172+
unsafe { MaybeUninit::slice_assume_init_ref(this) }
1173+
}
1174+
1175+
/// Fills `this` with elements by copying `value`, returning a mutable
1176+
/// reference to the now initialized contents of `this`.
1177+
///
1178+
/// This is similar to [`slice::fill`] but is restricted to `Copy` values.
1179+
/// Use [`MaybeUninit::fill_cloned`] to initialize from a `Clone` value.
1180+
///
1181+
/// # Examples
1182+
///
1183+
/// ```
1184+
/// #![feature(maybe_uninit_fill)]
1185+
/// use std::mem::MaybeUninit;
1186+
///
1187+
/// let mut dst = [MaybeUninit::uninit(); 5];
1188+
/// let init = MaybeUninit::fill_mut(&mut dst, 0u8);
1189+
/// init[4] = 123;
1190+
///
1191+
/// assert_eq!(init, &[0, 0, 0, 0, 123]);
1192+
/// ```
1193+
#[unstable(feature = "maybe_uninit_fill", issue = "117428")]
1194+
pub fn fill_mut<'a>(this: &'a mut [MaybeUninit<T>], value: T) -> &'a mut [T]
1195+
where
1196+
T: Copy,
1197+
{
1198+
this.fill(MaybeUninit::new(value));
1199+
// SAFETY: Valid elements have just been copied into `this` so it is initialized
1200+
unsafe { MaybeUninit::slice_assume_init_mut(this) }
1201+
}
1202+
1203+
/// Fills `this` with elements by cloning `value`, returning a reference to
1204+
/// the now initialized contents of `this`. Any already initialized elements
1205+
/// will not be dropped.
1206+
///
1207+
/// This is similar to [`slice::fill`] but does not drop existing elements.
1208+
///
1209+
/// # Panics
1210+
///
1211+
/// This function will panic if the implementation of `Clone` panics.
1212+
///
1213+
/// If there is a panic, the already initialized elements will be dropped.
1214+
///
1215+
/// # Examples
1216+
///
1217+
/// ```
1218+
/// #![feature(maybe_uninit_fill)]
1219+
/// use std::mem::MaybeUninit;
1220+
///
1221+
/// let mut dst = [
1222+
/// MaybeUninit::uninit(),
1223+
/// MaybeUninit::uninit(),
1224+
/// MaybeUninit::uninit(),
1225+
/// ];
1226+
/// let msg = String::from("hello");
1227+
/// let init = MaybeUninit::fill_cloned(&mut dst, &msg);
1228+
///
1229+
/// assert_eq!(init, &["hello", "hello", "hello"]);
1230+
/// ```
1231+
#[unstable(feature = "maybe_uninit_fill", issue = "117428")]
1232+
pub fn fill_cloned<'a>(this: &'a mut [MaybeUninit<T>], value: &T) -> &'a [T]
1233+
where
1234+
T: Clone,
1235+
{
1236+
let len = this.len();
1237+
1238+
// guard is needed b/c panic might happen during a clone
1239+
let mut guard = CloneGuard { slice: this, initialized: 0 };
1240+
1241+
for i in 0..len {
1242+
guard.slice[i].write(value.clone());
1243+
guard.initialized += 1;
1244+
}
1245+
1246+
super::forget(guard);
1247+
1248+
// SAFETY: Valid elements have just been written into `this` so it is initialized
1249+
unsafe { MaybeUninit::slice_assume_init_ref(this) }
1250+
}
1251+
1252+
/// Fills `this` with elements returned by calling a closure repeatedly,
1253+
/// returning a reference to the now initialized contents of `this`. Any
1254+
/// already initialized elements will not be dropped.
1255+
///
1256+
/// This is similar to [`slice::fill_with`] but does not drop existing
1257+
/// elements.
1258+
///
1259+
/// # Panics
1260+
///
1261+
/// This function will panic if the closure panics.
1262+
///
1263+
/// If there is a panic, the already initialized elements will be dropped.
1264+
///
1265+
/// # Examples
1266+
///
1267+
/// ```
1268+
/// #![feature(maybe_uninit_fill)]
1269+
/// use std::mem::MaybeUninit;
1270+
///
1271+
/// let mut dst = [MaybeUninit::uninit(); 5];
1272+
/// let mut next = 0;
1273+
/// let init = MaybeUninit::fill_with(&mut dst, || { next += 1; next });
1274+
///
1275+
/// assert_eq!(init, &[1, 2, 3, 4, 5]);
1276+
/// ```
1277+
#[unstable(feature = "maybe_uninit_fill", issue = "117428")]
1278+
pub fn fill_with<'a, F>(this: &'a mut [MaybeUninit<T>], mut f: F) -> &'a [T]
1279+
where
1280+
F: FnMut() -> T,
1281+
{
1282+
let len = this.len();
1283+
let mut guard = CloneGuard { slice: this, initialized: 0 };
1284+
1285+
for i in 0..len {
1286+
guard.slice[i].write(f());
1287+
guard.initialized += 1;
1288+
}
1289+
1290+
super::forget(guard);
1291+
1292+
// SAFETY: Valid elements have just been written into `this` so it is initialized
1293+
unsafe { MaybeUninit::slice_assume_init_ref(this) }
1294+
}
1295+
1296+
/// Fills `this` with the contents of an iterator, returning a reference to
1297+
/// the now initialized contents of `this`. Any already initialized elements
1298+
/// will not be dropped.
1299+
///
1300+
/// # Panics
1301+
///
1302+
/// This function will panic if the iterator panics.
1303+
///
1304+
/// If there is a panic, the already initialized elements will be dropped.
1305+
///
1306+
/// # Examples
1307+
///
1308+
/// ```
1309+
/// #![feature(maybe_uninit_fill)]
1310+
/// use std::mem::MaybeUninit;
1311+
///
1312+
/// let mut dst = [MaybeUninit::uninit(); 5];
1313+
/// let mut iter = [1, 2, 3].into_iter().cycle();
1314+
/// let init = MaybeUninit::fill_from(&mut dst, iter);
1315+
///
1316+
/// assert_eq!(init, &[1, 2, 3, 1, 2]);
1317+
/// ```
1318+
#[unstable(feature = "maybe_uninit_fill", issue = "117428")]
1319+
pub fn fill_from<'a, I>(this: &'a mut [MaybeUninit<T>], mut iter: I) -> &'a [T]
1320+
where
1321+
I: Iterator<Item = T>,
1322+
{
1323+
let len = this.len();
1324+
let mut guard = CloneGuard { slice: this, initialized: 0 };
1325+
1326+
for i in 0..len {
1327+
match iter.next() {
1328+
Some(value) => {
1329+
guard.slice[i].write(value);
1330+
guard.initialized += 1;
1331+
}
1332+
None => break,
1333+
}
1334+
}
1335+
1336+
let init_len = guard.initialized;
1337+
super::forget(guard);
1338+
1339+
// SAFETY: Valid elements have just been written into `this` so it is initialized
1340+
unsafe { MaybeUninit::slice_assume_init_ref(&mut this[..init_len]) }
1341+
}
1342+
11631343
/// Returns the contents of this `MaybeUninit` as a slice of potentially uninitialized bytes.
11641344
///
11651345
/// Note that even if the contents of a `MaybeUninit` have been initialized, the value may still
@@ -1313,3 +1493,19 @@ impl<T, const N: usize> [MaybeUninit<T>; N] {
13131493
unsafe { intrinsics::transmute_unchecked(self) }
13141494
}
13151495
}
1496+
1497+
struct CloneGuard<'a, T> {
1498+
slice: &'a mut [MaybeUninit<T>],
1499+
initialized: usize,
1500+
}
1501+
1502+
impl<'a, T> Drop for CloneGuard<'a, T> {
1503+
fn drop(&mut self) {
1504+
let initialized_part = &mut self.slice[..self.initialized];
1505+
// SAFETY: this raw slice will contain only initialized objects
1506+
// that's why, it is allowed to drop it.
1507+
unsafe {
1508+
crate::ptr::drop_in_place(MaybeUninit::slice_assume_init_mut(initialized_part));
1509+
}
1510+
}
1511+
}

‎library/core/tests/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@
4949
#![feature(slice_from_ptr_range)]
5050
#![feature(slice_split_once)]
5151
#![feature(split_as_slice)]
52+
#![feature(maybe_uninit_fill)]
5253
#![feature(maybe_uninit_uninit_array)]
5354
#![feature(maybe_uninit_write_slice)]
5455
#![feature(maybe_uninit_uninit_array_transpose)]

‎library/core/tests/mem.rs

Lines changed: 212 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -308,21 +308,226 @@ fn uninit_write_slice_cloned_mid_panic() {
308308
}
309309
}
310310

311+
#[derive(Clone)]
312+
struct DropBomb;
313+
314+
impl Drop for DropBomb {
315+
fn drop(&mut self) {
316+
panic!("dropped a bomb! kaboom")
317+
}
318+
}
319+
311320
#[test]
312321
fn uninit_write_slice_cloned_no_drop() {
313-
#[derive(Clone)]
314-
struct Bomb;
322+
let mut dst = [MaybeUninit::uninit()];
323+
let src = [DropBomb];
324+
325+
MaybeUninit::write_slice_cloned(&mut dst, &src);
326+
327+
forget(src);
328+
}
329+
330+
#[test]
331+
fn uninit_fill() {
332+
let mut dst = [MaybeUninit::new(255); 64];
333+
let expect = [0; 64];
334+
335+
assert_eq!(MaybeUninit::fill(&mut dst, 0), &expect);
336+
}
337+
338+
#[test]
339+
fn uninit_fill_mut() {
340+
let mut dst = [MaybeUninit::new(255); 64];
341+
let expect = [0; 64];
342+
343+
let init: &mut [u8] = MaybeUninit::fill_mut(&mut dst, 0);
344+
assert_eq!(init, &expect);
345+
}
315346

316-
impl Drop for Bomb {
317-
fn drop(&mut self) {
318-
panic!("dropped a bomb! kaboom")
347+
#[test]
348+
fn uninit_fill_cloned() {
349+
let mut dst = [MaybeUninit::new(255); 64];
350+
let expect = [0; 64];
351+
352+
assert_eq!(MaybeUninit::fill_cloned(&mut dst, &0), &expect);
353+
}
354+
355+
struct IncrementUntilPanic {
356+
limit: usize,
357+
rc: Rc<()>,
358+
}
359+
360+
impl Clone for IncrementUntilPanic {
361+
fn clone(&self) -> Self {
362+
if Rc::strong_count(&self.rc) >= self.limit {
363+
panic!("expected panic on clone");
364+
}
365+
Self {
366+
limit: self.limit,
367+
rc: self.rc.clone(),
319368
}
320369
}
370+
}
321371

372+
#[test]
373+
#[cfg(panic = "unwind")]
374+
fn uninit_fill_cloned_mid_panic() {
375+
use std::panic;
376+
377+
let rc = Rc::new(());
378+
379+
let mut dst = [
380+
MaybeUninit::uninit(),
381+
MaybeUninit::uninit(),
382+
MaybeUninit::uninit(),
383+
];
384+
385+
let src = IncrementUntilPanic { limit: 3, rc: rc.clone() };
386+
let err = panic::catch_unwind(panic::AssertUnwindSafe(|| {
387+
MaybeUninit::fill_cloned(&mut dst, &src);
388+
}));
389+
390+
drop(src);
391+
392+
match err {
393+
Ok(_) => unreachable!(),
394+
Err(payload) => {
395+
payload
396+
.downcast::<&'static str>()
397+
.and_then(|s| if *s == "expected panic on clone" { Ok(s) } else { Err(s) })
398+
.unwrap_or_else(|p| panic::resume_unwind(p));
399+
400+
assert_eq!(Rc::strong_count(&rc), 1)
401+
}
402+
}
403+
}
404+
405+
#[test]
406+
#[cfg(panic = "unwind")]
407+
fn uninit_fill_cloned_no_drop() {
322408
let mut dst = [MaybeUninit::uninit()];
323-
let src = [Bomb];
409+
let src = DropBomb;
324410

325-
MaybeUninit::write_slice_cloned(&mut dst, &src);
411+
MaybeUninit::fill_cloned(&mut dst, &src);
412+
413+
forget(src);
414+
}
415+
416+
#[test]
417+
fn uninit_fill_with() {
418+
let mut dst = [MaybeUninit::new(255); 64];
419+
let expect = [0; 64];
420+
421+
assert_eq!(MaybeUninit::fill_with(&mut dst, || 0), &expect);
422+
}
423+
424+
#[test]
425+
#[cfg(panic = "unwind")]
426+
fn uninit_fill_with_mid_panic() {
427+
use std::panic;
428+
429+
let rc = Rc::new(());
430+
431+
let mut dst = [
432+
MaybeUninit::uninit(),
433+
MaybeUninit::uninit(),
434+
MaybeUninit::uninit(),
435+
];
436+
437+
let src = IncrementUntilPanic { limit: 3, rc: rc.clone() };
438+
let err = panic::catch_unwind(panic::AssertUnwindSafe(|| {
439+
MaybeUninit::fill_with(&mut dst, || src.clone());
440+
}));
441+
442+
drop(src);
443+
444+
match err {
445+
Ok(_) => unreachable!(),
446+
Err(payload) => {
447+
payload
448+
.downcast::<&'static str>()
449+
.and_then(|s| if *s == "expected panic on clone" { Ok(s) } else { Err(s) })
450+
.unwrap_or_else(|p| panic::resume_unwind(p));
451+
452+
assert_eq!(Rc::strong_count(&rc), 1)
453+
}
454+
}
455+
}
456+
457+
#[test]
458+
#[cfg(panic = "unwind")]
459+
fn uninit_fill_with_no_drop() {
460+
let mut dst = [MaybeUninit::uninit()];
461+
let src = DropBomb;
462+
463+
MaybeUninit::fill_with(&mut dst, || src.clone());
464+
465+
forget(src);
466+
}
467+
468+
#[test]
469+
fn uninit_fill_from() {
470+
let mut dst = [MaybeUninit::new(255); 64];
471+
let src = [0; 64];
472+
473+
assert_eq!(MaybeUninit::fill_from(&mut dst, src.into_iter()), &src);
474+
}
475+
476+
#[test]
477+
#[cfg(panic = "unwind")]
478+
fn uninit_fill_from_mid_panic() {
479+
use std::panic;
480+
481+
struct IterUntilPanic {
482+
limit: usize,
483+
rc: Rc<()>,
484+
}
485+
486+
impl Iterator for IterUntilPanic {
487+
type Item = Rc<()>;
488+
fn next(&mut self) -> Option<Self::Item> {
489+
if Rc::strong_count(&self.rc) >= self.limit {
490+
panic!("expected panic on next");
491+
}
492+
Some(self.rc.clone())
493+
}
494+
}
495+
496+
let rc = Rc::new(());
497+
498+
let mut dst = [
499+
MaybeUninit::uninit(),
500+
MaybeUninit::uninit(),
501+
MaybeUninit::uninit(),
502+
MaybeUninit::uninit(),
503+
];
504+
505+
let src = IterUntilPanic { limit: 3, rc: rc.clone() };
506+
507+
let err = panic::catch_unwind(panic::AssertUnwindSafe(|| {
508+
MaybeUninit::fill_from(&mut dst, src);
509+
}));
510+
511+
match err {
512+
Ok(_) => unreachable!(),
513+
Err(payload) => {
514+
payload
515+
.downcast::<&'static str>()
516+
.and_then(|s| if *s == "expected panic on next" { Ok(s) } else { Err(s) })
517+
.unwrap_or_else(|p| panic::resume_unwind(p));
518+
519+
assert_eq!(Rc::strong_count(&rc), 1)
520+
}
521+
}
522+
}
523+
524+
#[test]
525+
#[cfg(panic = "unwind")]
526+
fn uninit_fill_from_no_drop() {
527+
let mut dst = [MaybeUninit::uninit()];
528+
let src = [DropBomb];
529+
530+
MaybeUninit::fill_from(&mut dst, src.iter());
326531

327532
forget(src);
328533
}

0 commit comments

Comments
 (0)
Please sign in to comment.