This is very common in embedded contexts, where you can take nothing for granted.
1. no_std (like no libc + no malloc in C)
2. no_std + alloc (like no libc + malloc in C)
3. std (like a full libc + malloc in C)
The difference between 1 + 2 is like three lines. The difference between 2 + 3 is a change to the entire standard library. ATM only ESP32 devices support option 3 (they build a standard library implementation on top of FreeRTOS/ESP-IDF).
* file and other I/O, including filesystem ops
* access to system time
* threads
* collections and some other things that require an allocator (not many things actually do in Rust’s stdlib!)
* floating-point functions (the types themselves and builtin operators work fine)
`alloc` gives you `Vec`, `String`, `Box`, `BTreeMap/Set`, ref-counted pointers, and a few `Vec`-derived collections like `VecDeque`. Very annoyingly not `HashSet/Map` though, due to a literally single-line dependence on a system entropy source which happens to not be easily factorable out because reasons.
Currently the `sys` crate implementation is hard-coded into the compiler but eventually you will be able to provide it without modifying the compiler so you can e.g. target a RTOS or whatever.
It looks like that work started really recently actually:
https://github.com/rust-lang/rust/commit/99128b7e45f8b95d962...
It's also the case that such targets often can't take advantage of the advanced features (like multithreading optimisations) that "full fat" allocator provide.
Zig having allocators totally opaque is interesting and you get to learn a lot if you dig deeper. Tons of different designs. Allocators on top of allocators. Tiny ones for small bundles. Non deallocating ones for one shot apps. Stuff you never care about in a GC environment.
I suggest checking it out if it interests you.