Description
Proposal
Problem statement
People are often unsure about what the rules are for creating a ZST. See, for example, https://internals.rust-lang.org/t/equality-of-zst-values/19815/3?u=scottmcm, where the person made an unsound function. (Thankfully they made the thread to ask about it, rather than just assuming it was fine!)
There's lots of ways that you could make a ZST: mem::uninitialized::<Z>()
, mem::zeroed::<Z>()
, MaybeUninit::<Z>::uninit().assume_init()
, *NonNull::<Z>::dangling().as_ptr()
, mem::transmute_unchecked::<_, Z>(())
, etc.
It would be nice to have one obvious API to point people at that would also serve as a place to put documentation about the safety and validity invariants of ZSTs, and thus the necessary preconditions for calling the method.
Motivating examples or use cases
unsafe
code often has different paths for ZSTs where it wants to make items, such as
https://github.com/rust-lang/rust/blob/9bd71afb90c2a6e0348cdd4a2b10a3bf39908f19/library/alloc/src/vec/into_iter.rs#L196-L197
This can show up in const generic code making arrays, too, where in an if N == 0 { return []; }
doesn't work, so a "make a ZST" API could be nice to use there too:
https://github.com/rust-lang/rust/blob/4654a910018cf0447db1edb2a46a0cae5f7dff8e/library/core/src/array/mod.rs#L801-L804
Solution sketch
mod mem {
pub unsafe fn conjure_zst<T>() -> T;
}
The implementation of which would use (directly or indirectly) things like https://doc.rust-lang.org/nightly/std/intrinsics/fn.assert_inhabited.html to give good error messages for hitting conjure_zst::<!>()
. And it would probably have a runtime trap (ud2
) for it not actually being a ZST, since that's checkable at monomorphization time, and thus would be cheap in practice.
The docs would have text like https://github.com/rust-lang/rust/pull/95385/files#diff-02049689ae3bb7ac98af3418ad8fdca219bfa1227cb6cad31afc72bdbae30e27R681-R717
Alternatives
- Enforce ZST-ness with a post-mono error, like a
const { assert!(T::IS_ZST) }
. I didn't pick that one because that would keep it from being used inif T::IS_ZST { ... }
blocks, like the array example. - Do nothing, since there's already lots of ways to make ZSTs.
- Also add reference versions to get
&'static mut Z
and such.
Links and related work
A few more threads I found:
https://internals.rust-lang.org/t/is-synthesizing-zero-sized-values-safe/11506?u=scottmcm
https://internals.rust-lang.org/t/static-mutable-references-to-zsts/18330?u=scottmcm
What happens now?
This issue contains an API change proposal (or ACP) and is part of the libs-api team feature lifecycle. Once this issue is filed, the libs-api team will review open proposals as capability becomes available. Current response times do not have a clear estimate, but may be up to several months.
Possible responses
The libs team may respond in various different ways. First, the team will consider the problem (this doesn't require any concrete solution or alternatives to have been proposed):
- We think this problem seems worth solving, and the standard library might be the right place to solve it.
- We think that this probably doesn't belong in the standard library.
Second, if there's a concrete solution:
- We think this specific solution looks roughly right, approved, you or someone else should implement this. (Further review will still happen on the subsequent implementation PR.)
- We're not sure this is the right solution, and the alternatives or other materials don't give us enough information to be sure about that. Here are some questions we have that aren't answered, or rough ideas about alternatives we'd want to see discussed.