```#![no_std]``` for ordinary apps

⚓ Rust    📅 2025-09-29    👤 surdeus    👁️ 7      

surdeus

Warning

This post was published 31 days ago. The information described in this article may have changed.

Hello everyone!

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:
fn main(argc: core::ffi::c_int, argv: *mut *mut core::ffi::c_char) -> core::ffi::c_int

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)

2 posts - 2 participants

Read full topic

🏷️ Rust_feed