no_std の Rust 環境
組み込みプログラミングという用語は、非常に幅広い異なる種類のプログラミングを指すために使われます。 数 KB の RAM と ROM しかない 8 ビット MCU(ST72325xx など) 向けのプログラミングから、Raspberry Pi のようなシステム (Model B 3+)までを含み、こちらは 32/64 ビットの 4 コア Cortex-A53 @ 1.4 GHz と 1GB の RAM を備えています。コードを書く際に適用される制約や制限は、 どのようなターゲットやユースケースを持つかによって異なります。
組み込みプログラミングには、一般的に 2 つの分類があります:
ホステッド環境
この種の環境は、通常の PC 環境に近いものです。 これは、ファイルシステム、ネットワーキング、メモリ管理、スレッドなど、 さまざまなシステムとやり取りするためのプリミティブを提供するシステムインターフェイス 例: POSIX が提供されることを意味します。 標準ライブラリは通常、その機能を実装するために、さらにこれらのプリミティブに依存します。 また、何らかの sysroot や RAM/ROM 使用量の制約、そして場合によっては 特殊なハードウェアや I/O を持つこともあります。全体として、特殊用途の PC 環境でコーディングしているような感覚です。
ベアメタル環境
ベアメタル環境では、あなたのプログラムより前にコードは何もロードされていません。
OS が提供するソフトウェアがなければ、標準ライブラリをロードすることはできません。
その代わり、プログラムとそれが使用するクレートは、実行のためにハードウェア(ベアメタル)のみを利用できます。
Rust が標準ライブラリをロードしないようにするには no_std を使用します。
標準ライブラリのプラットフォーム非依存な部分は libcore を通じて利用できます。
libcore は、組み込み環境では常に望ましいとは限らないものも除外しています。
その 1 つが、動的メモリ割り当てのためのメモリアロケータです。
これやその他の機能が必要な場合、それらを提供するクレートが用意されていることがよくあります。
libstd ランタイム
前述のとおり、libstd を使用するには何らかのシステムとの統合が必要ですが、これは単に
libstd が OS 抽象化にアクセスするための共通の方法を提供しているからだけではなく、
ランタイムも提供しているためです。
このランタイムは、とりわけ、スタックオーバーフロー保護の設定、コマンドライン引数の処理、
そしてプログラムの main 関数が呼び出される前にメインスレッドを生成することを担当します。このランタイムも no_std 環境では利用できません。
まとめ
#![no_std] は、クレートが std クレートではなく core クレートにリンクすることを示すクレートレベル属性です。
一方、libcore クレートは、プログラムが実行されるシステムについて何も仮定しない
std クレートのプラットフォーム非依存なサブセットです。
そのため、浮動小数点数、文字列、スライスといった言語プリミティブ向けの API に加えて、
アトミック操作や SIMD 命令のようなプロセッサ機能を公開する API も提供します。ただし、プラットフォーム統合を伴うもののための API は欠いています。
これらの特性により、no_std と libcore のコードは、ブートローダー、ファームウェア、カーネルのような
あらゆる種類のブートストラップ(stage 0)コードに使用できます。
概要
| 機能 | no_std | std |
|---|---|---|
| ヒープ(動的メモリ) | * | ✓ |
| コレクション(Vec、BTreeMap など) | ** | ✓ |
| スタックオーバーフロー保護 | ✘ | ✓ |
| main の前に初期化コードを実行 | ✘ | ✓ |
| libstd が利用可能 | ✘ | ✓ |
| libcore が利用可能 | ✓ | ✓ |
| ファームウェア、カーネル、またはブートローダーのコードを書くこと | ✓ | ✘ |
* alloc クレートを使用し、alloc-cortex-m のような適切なアロケータを使う場合に限ります。
** collections クレートを使用し、グローバルなデフォルトアロケータを設定する場合に限ります。
** HashMap と HashSet は、安全な乱数生成器がないため利用できません。