Rustのno_std環境

組込みプログラミングという用語は、様々な分野のプログラミングに使用されます。 たった数キロバイトのRAMかROMが付随する8ビットMCU (例えば、ST72325xx)から、32/64ビットの4コア Cortex-A53 @ 1.4GHzと1GBのRAMが搭載されたRaspberry Pi(Model B 3+)のようなシステムまで、幅広いです。 どのような種類の目的とユースケースがあるか、によって、コードを書くときに異なる制限/限界が課されます。

2つの一般的な組込みプログラミングの分類があります。

ホストされた環境

この分類の環境は、普通のPCの環境に近いです。 これの意味するところは、POSIXのようなシステムインタフェースが提供されている、ということです。システムインタフェースは、ファイルシステムやネットワーク、メモリ管理、スレッドといった多様なシステムとやりとりするための基本要素を提供します。 通常、標準ライブラリは、その機能を実装するために、これらの基本要素に依存します。 また、sysrootや、RAM/ROM利用の制限、そしておそらく特別なハードウェアやIOがあるかもしれません。 全体としては、特殊な用途のPC環境でコーディングをするようなものです。

ベアメタル環境

ベアメタル環境では、高機能なOSが動作していて、私たちのコードをホスティングしてくれる、ということはありません。 これは、基本要素がないことを意味しており、それ故に、デフォルトでは標準ライブラリもありません。 コードにno_stdのマーキングをすることで、そのコードが、ベアメタル環境で実行できることを示します。 no_stdなコードからは、Rustのlibstdとメモリの動的確保が使えません。 しかしながら、no_stdなコードでもlibcoreを使うことができます。libcoreは、ほんの数種類のシンボルを提供することで、いかなる環境でも容易に利用することができます (詳細は、libcoreを参照して下さい)。

libstdランタイム

上述の通り、libstdの利用には、いくらかのシステムインテグレーションが必要です。しかし、これはlibstdがOSの抽象にアクセスするための共通の方法を提供しているだけでなく、ランタイムも提供しているためです。 ランタイムは、とりわけ、スタックオーバーフロープロテクションの準備、コマンドライン引数の処理、メインスレッドの生成、をプログラムのメイン関数が呼び出される前に処理します。 このランタイムも、no_std環境では利用できません。

まとめ

#![no_std]は、クレートレベルの属性で、そのクレートがstdクレートの代わりにcoreクレートとリンクすることを意味します。 libcoreクレートは、プラットフォームに依存しないstdクレートのサブセットです。libcoreクレートは、プログラムが動作するシステムについて前提を置きません。 libcoreクレートは、浮動小数点、文字列やスライスといった言語の基本要素となるAPIと、アトミック操作やSIMD命令といったプロセッサの機能を公開するAPIとを、提供します。一方、プラットフォームインテグレーションを伴うようなAPIは欠如しています。 これらの特性のため、no_stdとlibcoreのコードは、ブートローダー、ファームウェア、カーネルといったあらゆるブートストラップ (ステージ0)のコードにも利用できます。

概略

機能no_stdstd
ヒープ (動的メモリ)*
コレクション (Vec, HashMap, など)**
スタックオーバーフロープロテクション
main関数前の初期化コード実行
libstdの利用
libcoreの利用
ファームウェア、カーネル、ブートローダーのコードを書く

* allocクレートを使い、[alloc-cortex-m]のような適切なアロケータを使った場合のみ

** collectionsクレートを使い、グローバルなデフォルトアロケータを設定した場合のみ

参照