Skip to content

peter-kehl/no_std_libs

Repository files navigation

Slides and alternative navigation

If you are seeing this, consider viewing presentation slides (online) instead. Or see README-NAVIGATE-SLIDES.md (online) for alternatives.


Audience, Purpose, Scope

Audience and purpose

  • general Rust developers moving to low level or no_std
  • non-Rust low level developers moving to Rust

Disambiguation of std

Rust has #![no_std] declaration, but it doesn't have #![std]. Instead, if you don't have #![no_std] at the top of your crate, availability of std library is implied.

Here we use std to refer to either

  • Rust std library, or
  • crates not declared as no_std (that is, crates that can use Rust std library).

Scope > In

  • developing no_std library crates (without std library), with or without heap

Scope > Limited

Scope > Limited: Cross-platform & features


Scope > Out


Scope > Out > Unsafe


Prerequisites


no_std

A Rust no_std crate can work with, or without, heap. Either way, it


no_std > Without Heap


no_std > With Heap


no_std > Any

Any no_std code (whether heapless or with heap) is limited:


Embrace Nightly

  • Embrace nightly channel (version) of Rust. no_std development is challenging enough. Help yourself by new, often ergonomical, features of the language & core library API (for example, #[bench]). A lot of nightly API has become part of beta and stable (and anything new goes through nightly and stable, anyway). See the Rust Forge schedule.

    Also, embedded devices often have no/restricted connectivity, and no other software running, so nightly may be secure enough. Plus, any upgrades replace the whole application, so even if nightly API changes, nothing outside of your embedded application changes (if the only change was in the Rust core or ABI). You you can go ahead and apply such changes bravely.

    If you need beta or nightly, specify it per-project in rust-toolchain.toml > [toolchain]. See also nightly Rust, channels, Rustup overrides and Rustup profiles.

    Because of that, all Rust links here to Rust core/alloc API, the Rust book and the Cargo book have a nightly prefix. The documentation clearly mentions which parts are nightly (or beta) only, anyway. If you uwant to access it as beta then change the nightly prefix to beta; or see the stable by removing the prefix.

    See also the Rust RFC book.









Unit Tests

Even if a library itself is no_std, its unit tests (ones in modules marked with #[cfg(test)]) are in a separate crate (auto-generated by cargo test). Hence the tests can use alloc, and full std, too.

However, you'll need extern crate std; in every test "top level" module (a module which has [cfg(test)] in front of its mod keyword in its parent (non-test) module).


Builds and Integration Tests

  • No simple way to run/debug no_std binaries on desktop.
  • Workaround: Separate sets of crates: *_build for verification (on no_std targets); *_test for testing (on desktop).

Timestamped Nightly

Suggest not to specify channel = "nightly" in rust-toolchain.toml. Why? Occasionally some targets are not available (on nightly), unfortunately. To prevent that, look up the rustup components history. Then in your rust-toolchain.toml > [toolchain] have (for example) channel = "nightly-2022-08-27".

However, only numeric Rust versions qualify for the minimum supported Rust version - in your crate's Cargo.toml > [package] > rust-version. If you need nightly, you can run rustc --version. Then specify its numeric version (excluding -nightly) in Cargo.toml > [package] > rust-version. And put a timestamped channel = "nightly-YYYY-MM-DD" in rust-toolchain.toml > [toolchain].

As of August 2022, these no_std targets seem to build smoothly: aarch64-unknown-none-softfloat, aarch64-unknown-none, x86_64-unknown-none, riscv32i-unknown-none-elf, riscv32imac-unknown-none-elf, riscv32imc-unknown-none-elf, riscv64gc-unknown-none-elf, riscv64imac-unknown-none-elf.


More & Takeaway for std

More

See

  • no_std_data: Patterns on no_std data handling. Code is mostly done, but slides are work in progress.
  • rust_incompatible_features: When std, no_std_bare and no_std_heap features (and potentially other features, as needed) benefit from being mutually exclusive/incompatible.

Takeaway for std

  • Import core:: and alloc:: (instead of std::) wherever you can - even in std development. That brings awareness about what parts of your library are no_std-friendly. std re-exports core and alloc parts, so your library (whether no_std or std) will automatically work with crates that import the same symbols from std.