Description
This is a list issues related to #![no_std]
that embedded developers tend to encounter in
practice. The main goal of this ticket is to make the portability WG aware of these issues.
- There are a lot of ergonomics issues and papercuts related to the use of
#![no_std]
. Addressing ergonomics around Rust and embedded/no_std development #26 goes
into detail but to summarize the main problems:
-
Adding
#![no_std]
to a crate doesn't mean it doesn't depend onstd
. If a dependency depends on
std
(i.e. it's not#![no_std]
) then the top crates does as well. The only way to be really
sure is to compile the crate for a target that doesn't havestd
in its sysroot (e.g. using
Xargo). -
The current practice for providing features that depend on
std
in a crate is to provide them
behind an opt-outstd
Cargo feature. This means thatno_std
developers have to do extra work
to depend on such crate: they have to disable thestd
feature viadefault-features = false
plus re-enabling all the other default features. -
A lot of crates on crates.io are not compatible with
#![no_std]
because they depend onstd
even though they don't necessarily need to, e.g. the author just forgot to add#![no_std]
to the
crate. This section of my embedded Rust in 2018 blog post goes into more detail
about the problem.
- It's hard to find
no_std
crates on crates.io. There's a no-std category on crates.
io but not everyone uses it, or is aware of it (see previous bullet). It would be better if
no_std
-ness was checked and displayed on crates.io without human intervention.
- Supporting both
std
,no_std
andno_std
+alloc
is a tough job. Just having to deal with the
differences in the core vs std prelude is a lot of work. For example, see the manually crafted
prelude that the serde project is using.
All these issues might disappear or be fixed with the elimination of the std
facade and
#![no_std]
, or they might not. I do not know.
- Even if the
std
facade and#![no_std]
are gone it's pretty important that libraries support a
no dynamic memory allocation mode. Microcontrollers are resource constrained devices and
sometimes a memory allocator is too heavyweight a dependency; also there application spaces (e.g.
safety critical) where dynamic memory allocation is downright banned (cf. MISRA standard).
It's already hard to figure out whether something allocates or not. #![no_std]
and the lack of
extern crate alloc
is a good indicator that a crates doesn't allocate. Not compiling the alloc
crate as part of the Xargo sysroot is a sure way to exclude the memory allocator. I'm afraid it may
become impossible to tell whether a dependency allocates if #![no_std]
and the std
facade are
gone.
I don't know if the portability lint could help with this (#![deny(alloc)]
?) or if we should have
a guideline about making a no dynamic memory allocation mode available via a Cargo feature then you
can simply look for the presence of such Cargo feature, but I guess it would be hard to make sure
everyone follows the guideline.
- Math support in
no_std
.
sqrt
, sin
and friends are not available in no_std
programs. In std
these functions come
from the system libm.a
, which is a C dependency. It's not convenient to use a C implementation in
no_std
because it requires you to get a full C toolchain (whereas normally you only need a linker)
that contains libm.a
(assuming there's a pre-compiled libm.a
available for your system) and
you'll likely will have to tweak the linker invocation to link the right libm.a
to your program
(e.g. the ARM Cortex-M toolchain ships with like 4 different libm.a
binaries compiled using
different profiles).
There are pure Rust implementations of libm
like the m
crate and math-rs
which are more
convenient to use as you don't need a full C toolchain or mess with the linking process. If the
std
facade is to be eliminated there should be some mechanism to be able to use such crate instead
of the C libm.a
that the std
crate pulls in.
Another alternative would be to have a rust-lang's Rust implementation of libm
but that's a lot
of work. Though such implementation could be done incrementally by compiling the functions not yet
implemented in Rust from some C code base (e.g. Julia's openlibm) which is the approach we are using
for compiler_builtins
.
compiler_builtins
.
In no_std
programs you have to explicitly link to the compiler_builtins
crate or at least one of
your dependencies has to. The std
crate depends on compiler_builtins
so you don't need a
explicit extern crate compiler_builtins
in std
programs.
compiler_builtins
is an implementation detail of the LLVM backend and no Rust user should ever
need to deal with it. Ideally if you link to core
then compiler_builtins
should also be linked
in but it's complicated.
The LLVM interface demands that the compiler intrinsics in compiler_builtins
are linked in as a
separate object file and that such object file is passed as the last object file argument to the
linker. The compiler_builtins
crate is marked with a special attribute (#![compiler_builtins]
,
iirc) so that this holds true even in the presence of LTO (where you would expect a single object
file to be produced -- due to compiler_builtins
you end with two object files).
Furthermore compiler_builtins
depends on core
so you can't make core
depend on
compiler_builtins
; also the core
crate isn't suppose to have any dependency. The separate object
file requirement also means that compiler_builtins
can't be merged into core
. This means that
even if you don't need anything other than core
you still have to depend on the forever unstable
compiler_builtins
crate to build a no_std
binary.
I don't know what are the plans of the portability WG wrt to the compiler_builtins
crate (it can't
be merged into std
for the same reason it can't be merged into core
) but from our point of view
it needs to disappear (*) (easier said than done) as it ties development of no_std
binaries to
the nightly channel.
(*) iirc, @eddyb had some ideas to put the compiler intrinsics in core
while preserving the
requirement of a separate object file but I don't remember the details.
- There are a few
no_std
forks of stuff that's provided bystd
. For example,cty
,
cstr_core
,hashmap_core
, etc. These should be provided by rust-lang to avoid code
duplication and bitrot (of the forks).
cc @jethrogb rust-lang-nursery/portability-wg#9
Community, if there's anything you think is missing from this list feel free to leave a comment
below and I'll add it to the list.