Recently, I was experimenting with creating a CLI #![no_std] programs (built a hello-world program in no-std mode). Though no-std mode is usually set for cases where std is not available, such as operating system kernel, an ordinary app may choose to opt out of using std as well. What I've noticed:
Since there is no std to handle start-up anymore, an app has to set #![no_main] and manually define a main() function, like:
Windows apps may have to use fn wmain() with wchar_t argument
An app may have to disable unwinding panics with panic=abort option for profiles and building with -Zbuild-std. Also, a panic handler must be defined
#![no_std] also disables alloc, but it can be re-enabled with extern crate alloc;. As far as I understand, alloc is merely a high-level interface to the memory allocator, it doesn't allocate memory itself (and if a crate uses it without std, it has to define a global allocator)
libc is not automatically linked, and the crate may fail to build due to missing symbols on which core depends -- it can be solved by adding compiler_builtins crate
I wonder, is there any benefit for apps that can use std, opt out of it? As far as I understand, there may be several benefits:
More control over start-up and shut-down process of an application, as well as environment and command-line argument processing
More control over memory allocation
Being able to opt-out of infallible memory allocation completely (however, as far as I understand, it can require either replacing alloc with your own interface and, as a result, losing the ability to use crates that depend on alloc, or enabling no_global_oom_handling option)
Smaller binary size (I've built two "hello, world" programs -- one with std and one without it, the ordinary one in --release mode resulted in a 437 kilobytes binary while no-std one resulted in a 8 kilobytes binary)
Well, you can always write Rust as if it were C. Used no_std. Use libc for everything you need from printf() upwards instead. Implement your own dynamic arrays (Vectors) etc. Just like the C folks do. If the borrow checker annoys you make everything "unsafe" and use raw pointers instead of references. Go nuts!
Use libc for everything you need from printf() upwards instead.
I don't see any reason to use printf() instead of Rust formatting system. In my "hello, world", I was using core::fmt::Write wrapper over write() function (on Linux).
It's a lot of fun.
"C-style" Rust may be a lot of fun (like code golfing), but it's not practical and may be harder to maintain than idiomatic Rust. When I was experimenting with #![no_std] in ordinary apps, I was trying to find practical uses of it (such as building a small binary that only includes what's necessary).
I certainly would not seriously recommend no-std and/or the "Crust" style for most desktop/server developers. "ordinary apps" as you say.
no-std is essential though for kernel development or small embedded systems and so on. I have worked on Linux systems that were small enough that using their libc in applications instead of hauling in the Rust standard library would have been essential.
Rust presents itself as a "systems programming language" which to my mind means you can use it to write systems like kernels from scratch. So I'm happy that there is no-std a way to write Rust as if it were C.