This is a question that I got stuck on a couple of month ago. My questions were answered in a disucssion with Shawn Tabrizi on GitHub, which was a big leap forward for me in understanding the architecture of substrate.
Why do we need this and in which cases would the no_std
attribute be applied?
Take a look at Architecture and the links therein. You'll find that a substrate node contains two version of the runtime code, the native runtime and the Wasm runtime (see https://en.wikipedia.org/wiki/WebAssembly). Note that the Wasm is stored on chain.
This is done to enable forkless runtime upgrades, in which the on-chain Wasm binary is replaced with a new version (but the native binary remains unchanged). Substrate uses Wasm as it is architecture independent (the Wasm must run on machines of different architecture, after all).
Now, whenever possible the faster native runtime is used to execute calls. But after a forkless runtime upgrade (in which only the Wasm updated), certain features of the native runtime may be outdated. Then the Wasm binary is used to execute the call, instead. See Executor for details.
Back to the no_std
attribute. When you build your node, the runtime/build.rs first builds the Wasm binary (see Build Scripts for details on build scripts) with the no_std
attribute. The reason for this is (again) architecture independence. Wasm cannot use the libstd
of Rust. The Embedded Rust Book puts it quite clearly:
In a bare metal environment no code has been loaded before your program. Without the software provided by an OS we can not load the standard library. Instead the program, along with the crates it uses, can only use the hardware (bare metal) to run.
In other words, libstd
is OS dependent, and that kind of thing doesn't fly with Wasm. Apparently you can use libstd
in Wasm, but this was rejected for a number of reasons: https://github.com/paritytech/substrate/issues/4043
So, after the Wasm binary is build, the runtime is compiled again, into the native binary, this time with std
enabled. The native binary actually contains the Wasm binary (we say that the Wasm is embedded into the native binary). This is done so that the first iteration of the Wasm binary can be copied from the native binary into the on-chain storage at genesis.
The inclusion of the Wasm is one example of where a block of code is excluded from the Wasm binary (for obvious reasons, in this case):
#[cfg(feature = "std")]
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
Other than that, it doesn't seem like many projects make much use of std
features directly, but in many of the standard dependencies you will frequently find #[cfg(feature = "std")]
, for example frame/system. The idea is that using objects only available in libstd
will improve the performance of the native binary.
Why would we check if the std
feature is not being used if it is implicitly always used via the default feature?
All dependencies (expect for the Wasm builder, which isn't a part of the runtime) are included using default-features = false
, which disables this behavior (see Cargo > Features > The default
feature). For example: runtime/Cargo.toml.
I believe this is done for the following reason:
- The Wasm builder doesn't use the default feature
std
. If you don't specify default-features = false
in the dependencies, then the dependencies use their std
feature, and then the Wasm can't be build.
- The "normal" build process (where you build the native binary) is done using the
std
feature, and you don't want to keep typing cargo build --features=std
.
But to be honest, I'm a little shaky on this last point. Maybe someone else can confirm or clarify further.
Gory Details
To elaborate on point about the Wasm builder not using std
: When looking at the source of substrate_wasm_builder, you'll find that the builder loads the runtime Cargo.toml
and calls wasm_project::create_and_compile
(see https://crates.parity.io/src/substrate_wasm_builder/builder.rs.html#244), which in turn filters out the std
feature (https://crates.parity.io/src/substrate_wasm_builder/wasm_project.rs.html#314-340).