Code change:
All existing standard library documentation implicitly assumes that the APIs are being used between the start of a Rust and end of . For example  does not document any indication that the function would panic. It does not need to document that, because the function cannot panic, as long as the call occurs within the duration of . However it's possible to observe a panic like this: (Related PR and discussion: ) In general using the standard library from an callback, or before through a static constructor, is UB: according to "we can't really guarantee anything specific happens [...]; at least not in a cross-platform way." Is this worth calling out centrally as a caveat to all other documentation of the standard library? At the top level of the whole crate (it would perhaps be more prominent than it deserves), at the module level, or in the Reference? Certainly for , , , the expectation users need to have is that nothing in there will work outside of . Are there APIs it makes sense to carve out as being permissible outside of ? Stuff like , , , , etc. We'd maybe need to do research into how constructors and atexit are being used in the wild. For example the crate relies on , , and to be usable before : It seems obvious that those things should work but there isn't documentation which guarantees it. I assume that makes the crate technically unsound as written.
IMHO, core types should work in whatever context but libstd is more dicey. It more explicitly has a runtime or three (e.g. Rust's, libc's and the OS's). So at a minimum anything in the modules , , , , , , , etc should be regarded with suspicion unless documented otherwise. In summary, my thoughts are: is fine depends on the allocator used assumes code is running in main (except for the and possibly stuff)
Core types can still panic and panics do IO.
Hm... how much of an issue is that? Panics don't have to do I/O (e.g. if stderr is closed) so if platforms don't support that before or after main then it can be silently skipped, no?
But do we do that properly today? And it's not just IO but also panic hooks which in turn can rely on various other parts of std.
I think it would be fine to document that e.g. after main they may want to set an empty (or aborting) panic hook if panicking is possible. I really don't want to be in a situation where the user is in a worse place than if they'd just used .
Could we turn panics into immediate aborts after main? That would also avoid the backtrace printing and symbolication.
I looked through the standard library, and is the only function that has limitation on usage before/after main (in this case, this is only a problem during TLS destruction). This function is also called from , and , which can therefore panic when called during TLS destruction. and might be able to be reworked to avoid this, but this is fundamentally a limitation of since it requires a reference to the current to work. The stack guard used by the stack overflow handler also accesses the same TLS slot as , but since the stack bounds don't have a this could be moved to a separate to properly support stack overflow handling during TLS destruction.
If we're going to make a statement at all then imo we should start out with something more cautious than making solid guarantees since this would be a pretty wide-ranging promise. before/after main behavior is not tested. It is best-effort and if someone relies on it they should have their own tests core and alloc are expected to work because they don't touch OS APIs or global state caveat: user code in any hookable API like panic handlers, global allocators, OOM handler or any future hooks. Especially panics affect a lot of code. we might replace hooks after main? most existing parts of std, backtrace and other things interacting with the OS are expected to work but there may be platform-specific edge-cases and the behavior may change in the future due to changing implementation details or new features that depend more runtime state (e.g. a std threadpool or async runtime) then finally a list of known limitations
Proposed documentation based on my previous comment:

Related discussion:
text: //! contains further primitive shared memory types, including [`atomic`] and //! [`mpsc`], which contains the channel types for message passing. //! <ins> //! # Use before and after `main()` //! //! Many parts of the standard library are expected to work before and after `main()`; //! but this is not guaranteed or ensured by tests. It is recommended that you write your own tests //! and run them on each platform you wish to support. //! This means that use of `std` before/after main, especially of features that interact with the //! OS or global state, is exempted from stability and portability guarantees and instead only //! provided on a best-effort basis. Nevertheless bug reports are appreciated. //! //! On the other hand `core` and `alloc` are most likely to work in such environments with //! the caveat that any hookable behavior such as panics, oom handling or allocators will also //! depend on the compatibility of the hooks. //! //! Some features may also behave differently outside main, e.g. stdio could become unbuffered, //! some panics might turn into aborts, backtraces might not get symbolicated or similar. //! //! Non-exhaustive list of known limitations: //! //! - after-main use of thread-locals, which also affects additional features: //!   - [`thread::current()`] //!   - [`thread::scope()`] //!   - [`sync::mpsc`] //! - before-main stdio file descriptors are not guaranteed to be open on unix platforms //! //! </ins> //! [I/O]: io //! [`MIN`]: i32::MIN //! [`MAX`]: i32::MAX