Change:
For a while it has been understood that the API is broken. Originally the intuitive understanding of this API was that it produced a fixed, arbitrary value. However (as extensively discussed elsewhere) uninitialized memory is not a “fixed, arbitrary value”, and that for nearly all types in Rust it is instantaneous undefined behavior for them to be uninitialized. What’s worse, even initialized values can be insta-UB. Rust uses its understanding of valid bit patterns to perform layout optimizations whereby invalid values can be repurposed as enum tags, which is how can be only a single word. Thus even ’s sibling is insta-UB when used with types like . As a result, was deprecated and replaced with , which avoids the problems of the former. In addition, both and were altered such that they will attempt to detect (and panic) when used on certain types: the former on types that must not be zero, and the latter on any types with invalid (defined) values. However, implementing these panic checks caused a great deal of breakage (which arguably is desirable for safety, although still extremely disruptive), and to reduce disruption the check is conservative instead of exhaustive (). Unfortunately, while improving the coverage of these checks will still leave as perfectly usable, will be rendered all but unusable, as essentially all types cannot ever be in an uninitialized state. This is a problem for legacy crates that were never migrated away from . However, there is a solution that both allows these legacy crates to compile while also avoiding the problem of invalid uninitialized values: can initialize with a valid value. This may seem contrary to the original intent of the API, but consider that the only reason to avoid initialization is performance, and that the choice is now between “my code doesn’t compile”, “my code contains undefined behavior”, and “my code is slower”; the latter is the most desirable outcome of the three. This raises the question: what value to initialize with? PR proposed the simplest option, which was to replace the innards of with , however zero is the value that is most often used for niche optimizations, so this would still reject a lot of code. But there is a more desirable alternative. Because Rust understands what values are invalid for a type—it must, in order to perform niche optimizations—it therefore should also understand which values are valid for a type. An intrinsic could be to the compiler which, given a type, produces an arbitrary valid value of that type. This intrinsic could be used within , and the existing panic check could be removed. This would allow all code in the wild still using to compile, and would also avoid all insta-UB related to validity invariants. <!-- TRIAGEBOTSTART --<!-- TRIAGEBOTASSIGNSTART --<!-- TRIAGEBOTASSIGNDATASTARTTRIAGEBOTASSIGNDATAEND --<!-- TRIAGEBOTASSIGNEND --<!-- TRIAGEBOTEND --
What if there is no valid value? currently helpfully panics with
I suspect it would not be a problem for it to continue to panic when used with uninhabited types.
An method for "get me an arbitrary value that meets the validity invariants but maybe not the safety ones" does sound potentially interesting, but if that's promised I think it should be a new API. I wouldn't want people to start calling to get that behaviour, and thus making their code do horrible things on older rusts without a compile-time break.
Sure, the intrinsic could be exposed via something like , although I'm not sure if it's more than academically useful, since after all you're still paying the cost of initialization and you'd still have to use .
claim
release-assignment
I am taking another shot at something like this in , with a slightly different approach than We keep all the existing panics. Things only change for uses of that do not (yet) panic. The general thrust currently is towards having panics in more cases, though not everyone is happy with that. If people want to reduce the amount of panics, I think we should discuss that separately from which bit pattern is actually being returned -- either in or in a new issue. We fill the resulting buffer with 0x01, not 0x00. This is not perfect, but it is noundef and valid for bool and all nonnull types. Only and some are out of luck. It's not quite a "valid" value, but that is not entirely possible anyway: we promise to LLVM that references are dereferenceable (whether they are used or not), so many existing uses of will never lead to well-defined LLVM IR.

Discussion:
#![feature(allow_internal_unstable)] #![feature(associated_type_bounds)] #![feature(auto_traits)] <ins> #![feature(cfg_sanitize)] </ins> #![feature(cfg_target_has_atomic)] #![feature(cfg_target_has_atomic_equal_alignment)] #![feature(const_fn_floating_point_arithmetic)]

/// produce a value of type `T`, while doing nothing at all. /// /// **This function is deprecated.** Use [`MaybeUninit<T>`] instead. <ins> /// It also might be slower than using `MaybeUninit<T>` due to mitigations that were put in place to /// limit the potential harm caused by incorrect use of this function in legacy code. </ins> /// /// The reason for deprecation is that the function basically cannot be used /// correctly: it has the same effect as [`MaybeUninit::uninit().assume_init()`][uninit].

// SAFETY: the caller must guarantee that an uninitialized value is valid for `T`. unsafe { intrinsics::assert_uninit_valid::<T>(); <del> MaybeUninit::uninit().assume_init() </del> <ins> let mut val = MaybeUninit::<T>::uninit(); // Fill memory with 0x01, as an imperfect mitigation for old code that uses this function on // bool, nonnull, and noundef types. But don't do this if we actively want to detect UB. if !cfg!(any(miri, sanitize = "memory")) { val.as_mut_ptr().write_bytes(0x01, 1); } val.assume_init() </ins> } }