|
7 | 7 |
|
8 | 8 | use crate::alloc::Layout;
|
9 | 9 | use crate::marker::DiscriminantKind;
|
| 10 | +use crate::panic::const_assert; |
10 | 11 | use crate::{clone, cmp, fmt, hash, intrinsics, ptr};
|
11 | 12 |
|
12 | 13 | mod manually_drop;
|
@@ -1407,3 +1408,60 @@ pub macro offset_of($Container:ty, $($fields:expr)+ $(,)?) {
|
1407 | 1408 | // The `{}` is for better error messages
|
1408 | 1409 | {builtin # offset_of($Container, $($fields)+)}
|
1409 | 1410 | }
|
| 1411 | + |
| 1412 | +/// Create a fresh instance of the inhabited ZST type `T`. |
| 1413 | +/// |
| 1414 | +/// Prefer this to [`zeroed`] or [`uninitialized`] or [`transmute_copy`] |
| 1415 | +/// in places where you know that `T` is zero-sized, but don't have a bound |
| 1416 | +/// (such as [`Default`]) that would allow you to instantiate it using safe code. |
| 1417 | +/// |
| 1418 | +/// If you're not sure whether `T` is an inhabited ZST, then you should be |
| 1419 | +/// using [`MaybeUninit`], not this function. |
| 1420 | +/// |
| 1421 | +/// # Panics |
| 1422 | +/// |
| 1423 | +/// If `size_of::<T>() != 0`. |
| 1424 | +/// |
| 1425 | +/// # Safety |
| 1426 | +/// |
| 1427 | +/// - `T` must be *[inhabited]*, i.e. possible to construct. This means that types |
| 1428 | +/// like zero-variant enums and [`!`] are unsound to conjure. |
| 1429 | +/// - You must use the value only in ways which do not violate any *safety* |
| 1430 | +/// invariants of the type. |
| 1431 | +/// |
| 1432 | +/// While it's easy to create a *valid* instance of an inhabited ZST, since having |
| 1433 | +/// no bits in its representation means there's only one possible value, that |
| 1434 | +/// doesn't mean that it's always *sound* to do so. |
| 1435 | +/// |
| 1436 | +/// For example, a library could design zero-sized tokens that are `!Default + !Clone`, limiting |
| 1437 | +/// their creation to functions that initialize some state or establish a scope. Conjuring such a |
| 1438 | +/// token could break invariants and lead to unsoundness. |
| 1439 | +/// |
| 1440 | +/// # Examples |
| 1441 | +/// |
| 1442 | +/// ``` |
| 1443 | +/// #![feature(mem_conjure_zst)] |
| 1444 | +/// use std::mem::conjure_zst; |
| 1445 | +/// |
| 1446 | +/// assert_eq!(unsafe { conjure_zst::<()>() }, ()); |
| 1447 | +/// assert_eq!(unsafe { conjure_zst::<[i32; 0]>() }, []); |
| 1448 | +/// ``` |
| 1449 | +/// |
| 1450 | +/// [inhabited]: https://doc.rust-lang.org/reference/glossary.html#inhabited |
| 1451 | +#[unstable(feature = "mem_conjure_zst", issue = "95383")] |
| 1452 | +pub const unsafe fn conjure_zst<T>() -> T { |
| 1453 | + const_assert!( |
| 1454 | + size_of::<T>() == 0, |
| 1455 | + "mem::conjure_zst invoked on a nonzero-sized type", |
| 1456 | + "mem::conjure_zst invoked on type {t}, which is not zero-sized", |
| 1457 | + t: &str = stringify!(T) |
| 1458 | + ); |
| 1459 | + |
| 1460 | + // SAFETY: because the caller must guarantee that it's inhabited and zero-sized, |
| 1461 | + // there's nothing in the representation that needs to be set. |
| 1462 | + // `assume_init` calls `assert_inhabited`, so we don't need to here. |
| 1463 | + unsafe { |
| 1464 | + #[allow(clippy::uninit_assumed_init)] |
| 1465 | + MaybeUninit::uninit().assume_init() |
| 1466 | + } |
| 1467 | +} |
0 commit comments