There is panic-analyzer [1] that searches for code that needlessly panics. You can also use the no-panic macro [2] to turn possible panics in a specific function (including main) into a compile error
Panic-analyzer looks like it is based on heuristics, searching for known-panicing APIs. I tried it on a workspace that uses io::stdout() and it did not flag this as potentially panicing.
No-panic looks nifty: it appears to be reliable, which is great. I wish there was an easy way to automatically apply this annotation to every single function in a given file or crate.
I think the article is wrong in that std::io::stdout would be panicking, or that "the panic is reachable somehow". It's just the optimizer doesn't see it doesn't panic.
But it only panicks if it is being used in a wrong way, which it isn't. The usage is contained within the implementation of std::io::stdout, so it's an implementation detail.
It's a shame that there are no better ways to eliminate panics in case they are impossible to trigger. The article shows some tricks, but I think the language is missing still some expressability around this, and the stdlib should also thrive harder to actually get rid of hard-to-optimize links to panic runtime in case of APIs that don't actually panic.
If the only problem was OnceLock, the simplest solution would be to replace it with a const initializer.
The main reason getting panic-free stdout is hard, is that the STDOUT initializers both call allocating constructors, and LineWriter doesn't have non-allocating constructors.
Thanks for the clarification on OnceLock::get_or_init. It's interesting to know that this is dynamically unreachable, but in a way that the compiler could not statically prove.
I 100% agree with your last paragraph. I would love if the language and stdlib made it easier to make panic-free binaries.
1: https://crates.io/crates/panic-analyzer
2: https://crates.io/crates/no-panic