no-std-compat 0.1.0

A `#![no_std]` compatibility layer that will make porting your crate to no_std *easy*.
Documentation

no-std-compat

A #![no_std] compatibility layer that will make porting your crate to no_std easy.

Why?

In Rust, you can disable the standard library (see here). Doing this gets rid of the normal std standard library and instead adds core, with an option to also add alloc for things requiring memory allocation. Using core + alloc results in something similar to the std, and many things using std can already be "ported" to use core + alloc.

But every single library written in rust needs to be updated. This is because the norm is to use std. Using core needs someone to break the norm, often only behind a feature flag. Compare this to Web Assembly, where almost only a few low-level crates like rand needs to care, because everything is still under std even though some features don't work there.

Many crates migrating to #![no_std] today write a small module called std that forwards imports libcore and liballoc together. This effort should be unified. We're stronger if not every single one of us needs to hit and figure out how to fix the same errors.

Usage

This library is designed to require as few lines of code as possible, so that these can be copy-pasted to a bunch of different libraries. My goal is to turn more crates into #![no_std] compatible. It also has in mind to support the std, as well as supporting no std, meaning you should only need few conditional compilation attributes.

Examples can be found in the examples/ folder.

  1. Add this crate to Cargo.toml, and enable any features you want to require (see next section).

Cargo.toml:

[dependencies]
no-std-compat = { version = "...", features = [ "alloc" ] }
  1. Optionally, add a std flag that pulls in the entire standard library and bypasses this compatibility crate. This is useful so you can use the standard library for debugging and for extra functionality for those who support it. The below code optionally adds the std feature as well to no-std-compat, which makes it just link to the standard library.

Cargo.toml:

[features]
default = [ "std" ] # Default to using the std
std = [ "no-std-compat/std" ]
  1. Optionally enable no_std, and import this crate renamed to std. This ensures all old imports still work on no_std. You could, of course, use any other name too. But this is what I would recommend.

src/lib.rs:

#![cfg_attr(not(feature = "std"), no_std)]

extern crate no_std_compat as std;
  1. Import the prelude in all files. This is because in no_std, rust removes the std import and instead only imports the core prelude. That is: Currently, it doesn't import the alloc prelude on its own. This also imports macros and other needed stuff.

src/**.rs:

use std::prelude::v1::*;

Optional features

  • alloc: This feature pulls in alloc and exposes it in all the usual locations. I.e std::collection gets mapped to alloc::collections and all the allocation stuff is added to the prelude.
  • std: This feature pulls in the entire standard library and overrides all other features. This effectively bypasses this crate completely. This is here to avoid needing feature gates: Just forward your optional std feature to here, we handle the rest.
  • compat_hash: This (perhaps a little hacky) feature remaps std::hash::Hash to std::cmp::Ord. It further links HashMap to BTreeMap. The point is so you can keep using the fast HashMap for those who have the standard library, and fall back to BTreeMap for those who do not. Be adviced, however, that this used in a public function signature could be confusing and should perhaps be avoided. But that is up to you!
  • compat_macros: This feature adds dummy println, eprintln, dbg, etc. implementations that do absolutely nothing. The point is that any debug functions or other loggings that are not required for the library to function, just stay silent in no_std.

Contributing

Updating the glue

Did you pull this crate and realize that it's outdated? Lucky for you, this crate came prepared. The glue can simply be regenerated with a python script.

Make sure you have the rust source downloaded somewhere. With rustup, it's a non-issue:

rustup component add rust-src

Now you can run ./generate.py > src/generated.rs. If it chooses the wrong rust version or maybe crashes all together, you can manually specify the source directory with --src. It's that easy. You can also, of course, run ./generate.py --help if you forgot the argument name.

Updating the feature list

If rust complains about a feature being required but not specified, or maybe about a feature being unused, this is because some imports are behind feature gates, and feature gates change. More often than not it is as trivial as adding or removing stuff from the long, long line in src/lib.rs that specifies features.

When using the std feature flag, no features are used and stable rust is possible.