If you are seeing this, consider viewing presentation slides (online) instead. Or see README-NAVIGATE-SLIDES.md (online) for alternatives.
- general Rust developers moving to low level or
no_std - non-Rust low level developers moving to Rust
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
stdlibrary, or - crates not declared as
no_std(that is, crates that can use Ruststdlibrary).
- This applies to both
no_stdandstd. - Rust can build for various targets (architectures/environments). But Cargo documentation keeps it
hidden in Cargo book's
FAQ
how to specify a target:
-
.cargo/config.toml-->build.target, for example:[build] target = "aarch64-unknown-none-softfloat"
-
- Architectures supported by Rust are in three
tiers. See also the rustc
book > Platform
Support. Beware many Tier 2
targets wouldn't build a simple (
no_std) application - not even heapless. - The Cargo book > Platform specific dependencies
- Per-platform build/linking configuration: have
.cargo/config.toml. - features: Compile
time-selectable subsets of library
crates.
no_stdbinaries- hardware, deployment, embedded debugging
- low level development in general (techniques, architectures, tools)
- specifics of real
time
applications and use with RTOS (Real Time OS)
- Rust is suitable for real time (because of no garbage collection & compilation)
- this applies to both
no_stdandstd
wasm(Web Assembly), althoughno_stdcrates are usually wasm-friendly- ABI (Application Binary Interface),
especially
#[no_mangle]- type layout and the
Rustonomicon (Unsafe Rust) > Alternative
Representations.
- Rustonomicon has parts applicable to safe and/or
stdRust, too. One of its gems: Higher-Rank Trait Bounds.
- Rustonomicon has parts applicable to safe and/or
async/awaitin no_std- particular
no_std-compatible crates
unsafecode- seemingly easier in embedded, because of no threads & no other applications/processes. But that may lead to hidden bugs that show up only once using the same code in non-embedded later.
- FFI (Foreign Function Interface)/Interoperability
- Rust and Cargo & dependencies in general
- no need for low level experience
- common (and a few uncommon) aspects of general Rust, especially
- setting up a project and basics of cargo
- package layout
- dependencies
- features
- architecture or feature-based conditional
compilation with
#[cfg(...)]attribute.
- experience with a statically typed and compiled language
- basics of linking, heap and stack
- Rust installation including
rustupandcargo(the recommended way) - Linux/Mac OS/Unix file path notation
A Rust no_std crate can work with, or without, heap. Either way, it
- is for low level (without an operating system; or it serves as a part of an OS kernel)
- starts with a
#![no_std]line at the top of your crate (lib.rsor a top level source file for a binary). See also Rust glossary >packageand Rust glossary >target. - if binary, it has no default fatal error
(
panic) handler; you must either- set it to
abort, or - use one of existing (embedded-friendly)
panic_handler-defining crates
- set it to
- provide a custom
panic_handler - if binary, and it uses heap, provide a global allocator
- has no access to
stdlibrary - no module paths starting withstd::, but - has a limited subset of
stdavailable ascoreandalloc. For example:
- all data is on stack, or static
- common for microcontrollers
- no availability of
- only if you have an
allocator-
register
#[global_allocator] -
then use
alloclibrary -
allocis equivalent to the respective subset ofstd. Actually,stdre-exportsallocandcoreparts. -
alloc::boxed::Box,alloc::vec::Vec,vec!macro (import it withuse alloc::vec;),alloc::collections::VecDeque,alloc::string::String,alloc::rc::Rc,alloc::sync::Arc -
alloc::collections::BTreeSet,alloc::collections::BTreeMapif your items/keys implementcore::comp::Ord.Even if our
struct(orenum) instances can't be ordered in human terms, or if the actual order doesn't matter for us, we could define some (predictable) order and useBTreeSet/BTreeMapfor most types.
-
Any no_std code (whether heapless or with heap) is limited:
- no
std::collections::HashSet, norstd::collections::HashMap(since computing hashes needs a source of entropy). - no
std::thread::Thread(and no multi-threading)
-
Embrace
nightlychannel (version) of Rust.no_stddevelopment is challenging enough. Help yourself by new, often ergonomical, features of the language &corelibrary API (for example,#[bench]). A lot ofnightlyAPI has become part ofbetaandstable(and anything new goes throughnightlyandstable, anyway). See the Rust Forge schedule.Also, embedded devices often have no/restricted connectivity, and no other software running, so
nightlymay be secure enough. Plus, any upgrades replace the whole application, so even ifnightlyAPI changes, nothing outside of your embedded application changes (if the only change was in the Rustcoreor ABI). You you can go ahead and apply such changes bravely.If you need
betaornightly, specify it per-project inrust-toolchain.toml>[toolchain]. See also nightly Rust, channels, Rustup overrides and Rustup profiles.Because of that, all Rust links here to Rust
core/allocAPI, the Rust book and the Cargo book have anightlyprefix. The documentation clearly mentions which parts arenightly(orbeta) only, anyway. If you uwant to access it asbetathen change thenightlyprefix tobeta; or see thestableby removing the prefix.See also the Rust RFC book.
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).
- No simple way to run/debug
no_stdbinaries on desktop. - Workaround: Separate sets of crates:
*_buildfor verification (onno_stdtargets);*_testfor testing (on desktop).*_ok_std_*,*_no_std_bare_*(heapless) and*_no_std_heap_*build with the relevant functionality.- Can't use a workspace for these alternatives (as all crates in a workspace share dependencies with same features).
- So, have the above builds in a separate crate each, under a directory like
test_crates/. *_testcrates re-use (some of) the tests. Those are centralized under*_any_std_test_*.- Suggest their folder names to start with a prefix based on/equal to the main crate. That makes navigation easier (in VS Code) when you open/compare... multiple main crates with similar-sounding sets of build & test crates.
- For an example see
ranging-rs/slicing-rs>test_crates:slicing_any_std_test(tests are not run directly here, but are shared with the following*_testcrates)slicing_no_std_bare_buildslicing_no_std_bare_testslicing_no_std_heap_buildslicing_no_std_heap_testslicing_ok_std_test
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.
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_bareandno_std_heapfeatures (and potentially other features, as needed) benefit from being mutually exclusive/incompatible.
- Import
core::andalloc::(instead ofstd::) wherever you can - even instddevelopment. That brings awareness about what parts of your library areno_std-friendly.stdre-exportscoreandallocparts, so your library (whetherno_stdorstd) will automatically work with crates that import the same symbols fromstd.