組み込みの世界は長らくC言語のものでした。eightも仕事で触れてきましたし、これは今も変わっていません。
一方で、Rustが組み込みでも使えるという話は何年も前から聞こえてきます。Rustをはじめてみたで言語そのものは触りましたし、QEMUで学ぶベアメタル開発ではRustで書いたコードを実機なしで動かすところまでやりました。
ただ、そこから先の「で、実務で使えるのか」がずっと曖昧なままでした。「使える」と言う人も「まだ無理」と言う人もいて、どちらも正しそうに聞こえます。
調べていくうちに、意見が割れる理由が分かりました。みんな違う層の話をしていたのです。
「牙城」は1枚岩ではなく、3層でした
- ① 書けるか … もう書ける。今回3つの書き方すべてを動かしました
- ② 安全認証を通せるか … ★2026年に動いた層。ここが今回いちばんの発見
- ③ 現場が乗り換えるか … まだ動いていない。ここが本丸
「崩れるのか」への答えは、「①はもう崩れた。②が崩れかけている。③は当分崩れない」になります。
調べるだけでは実感が湧かないので、実際に3つの書き方でプログラムを作り、QEMUで動かして測りました。そこで出てきた数字が、意外と示唆に富んでいます。
「牙城は崩れるのか」は、そのままでは答えられない
まず、この記事で使う3層の枠組みを示しておきます。
「Rustは組み込みで使える」と言う人は①を見ていて、「まだ無理」と言う人は③を見ています。どちらも間違っていません。そして②が、この1年で明確に動いた層です。
用語集
| 用語 | 平たく言うと | なぜ効くのか |
|---|---|---|
| ベアメタル | OSを載せず、マイコンの上で直接動かすこと | 組み込みの多くはこれ。使えるメモリが数KB〜数百KBの世界 |
| no_std | Rustの標準ライブラリを使わないモード | OSの機能を前提にした部分が外れる。組み込みでは必須 |
| Cortex-M | ARM社のマイコン向けCPUの系列 | 組み込みで最も広く使われている。今回はM3で検証 |
| ベクタテーブル | 電源が入ったとき最初にどこへ飛ぶかを書いた表 | メモリの先頭に置く決まり。★今回ここでつまずいた |
| セミホスティング | マイコン側の処理を、繋がっているPC側に肩代わりさせる仕組み | 画面のないマイコンでも文字を出せる。デバッグの定番 |
| HAL | Hardware Abstraction Layer。チップの違いを吸収する層 | これが統一されると、同じコードが別のチップで動く |
| ISO 26262 / ASIL | 車載の機能安全規格と、その危険度の等級(A〜D、Dが最も厳しい) | ★車に載せるソフトはこれを通す必要がある。Rustの最大の壁だった |
| AUTOSAR | 車載ソフトの標準的な作り。部品メーカー間の共通言語 | Rustに互換のRTOSが無いことが、車載での実質的な足かせ |
| クレート | Rustのライブラリの単位 | 依存が増えるほど、認証や監査の手間も増える |
今回の検証環境
実機のマイコンは使わず、Mac1台で完結しています。QEMUで学ぶベアメタル開発と同じやり方です。
| 項目 | 内容 |
|---|---|
| マシン | MacBook Pro(Apple Silicon M4) |
| Rust | rustc 1.97.1 / cargo 1.97.1 |
| ターゲット | thumbv7m-none-eabi(Cortex-M3向け・OSなし) |
| エミュレータ | QEMU 11.0.2(lm3s6965evb=Cortex-M3のボード) |
| お題 | 文字列を4行出して終了するだけ。3つの書き方で同じ動きを作る |
| 測ったもの | 依存クレート数/クリーンビルド時間(3回の中央値)/焼かれるサイズ/RAM使用量/行数 |
ターゲットの追加は、コマンド1つで1秒でした。
rustup target add thumbv7m-none-eabi
「組み込み向けの環境構築」と身構えていたのですが、クロスコンパイラを別途入れる必要すらありません。ここは拍子抜けするほど簡単でした。
★まず3つの書き方で、同じものを作って動かした
組み込みRustには、大きく3つの書き方があります。それぞれで同じプログラムを作りました。
① 素のベアメタル(依存クレートなし)
フレームワークを一切使わず、ベクタテーブルも自分で置く書き方です。
#![no_std]
#![no_main]
// ベクタテーブルは「スタックの初期値」と「リセット時の飛び先」の2つ
#[link_section = ".vector_table.stack"]
#[no_mangle]
pub static INITIAL_SP: u32 = 0x2001_0000;
#[link_section = ".vector_table.reset"]
#[no_mangle]
pub static RESET_VECTOR: unsafe extern "C" fn() -> ! = _reset;
#[no_mangle]
pub unsafe extern "C" fn _reset() -> ! {
sh_write0(b"hello from bare metal\n\0");
// …
}
セミホスティングの呼び出しもインラインアセンブラで自分で書きます。リンカスクリプトも手書きです。
② cortex-m-rt(定番の構成)
起動処理を cortex-m-rt に任せる、いちばん一般的な書き方です。
#![no_std]
#![no_main]
use cortex_m_rt::entry;
use cortex_m_semihosting::{debug, hprintln};
use panic_halt as _;
#[entry]
fn main() -> ! {
hprintln!("hello from cortex-m-rt");
for i in 0..3 {
hprintln!("count {}", i);
}
debug::exit(debug::EXIT_SUCCESS);
loop {}
}
ベクタテーブルもリンカスクリプトの大半も、クレートが用意してくれます。書くのは「やりたいこと」だけになりました。
③ Embassy(非同期フレームワーク)
近年の組み込みRustで一番勢いのある選択肢です。async で「待ち」を素直に書けます。
use embassy_executor::Spawner;
#[embassy_executor::task]
async fn counter() {
for i in 0..3 {
hprintln!("count {}", i);
}
debug::exit(debug::EXIT_SUCCESS);
}
#[embassy_executor::main]
async fn main(spawner: Spawner) {
hprintln!("hello from embassy");
spawner.spawn(counter()).unwrap();
}
今回は文字を出すだけなので恩恵が見えませんが、「センサーの応答を待つ」「通信の完了を待つ」といった処理が並ぶほど効いてきます。割り込みとステートマシンを手で書く代わりに、await で待てるからです。
3つとも、QEMUで同じ出力を出して正常終了しました。
$ qemu-system-arm -cpu cortex-m3 -machine lm3s6965evb -nographic \
-semihosting-config enable=on,target=native -kernel <バイナリ>
hello from cortex-m-rt
count 0
count 1
count 2
★測ってわかった、いちばん大事なトレードオフ
3つを同じ条件で測りました。
| 書き方 | 依存クレート | ビルド | 焼かれる量 | RAM | ソース |
|---|---|---|---|---|---|
| ①素のベアメタル | 0個 | 0.11秒 | 134 B | 0 B | 35行 |
| ②cortex-m-rt | 32個 | 2.42秒 | 2,636 B | 8 B | 14行 |
| ③Embassy | 54個 | 3.40秒 | 3,072 B | 56 B | 18行 |
※ 焼かれる量はELFのロード可能セグメントの合計(実際にFLASHへ書かれる量)です。ELFファイル自体は66KB〜136KBありますが、その大半はデバッグ情報で、マイコンには載りません。
★短く書けるほど、焼かれるサイズは大きくなる
35行 → 14行と書く量が6割減るのに、焼かれる量は 134B → 2,636B と約20倍になりました。手間をライブラリに預けたぶん、そのライブラリのコードが載るからです。
当たり前といえば当たり前ですが、「Rustは安全なぶん重い」のではなく、「楽をしたぶん重い」のだと数字で見えたのは収穫でした。素で書けば134バイトです。
ここで大事なのは、2.6KBが重いかどうかは、載せるマイコン次第ということです。フラッシュが256KBあるチップなら2.6KBは1%で誤差ですが、8KBしかないチップなら3分の1を持っていかれます。「どれが優れているか」ではなく「どの箱に入れるか」で決まります。
⚠つまずき:ベクタテーブルでコンパイラに止められた
①の素のベアメタルで、一度手が止まりました。
ベクタテーブルの先頭にはスタックの初期値(ただの数値)を、次にはリセット時に飛ぶ関数のアドレスを置きます。素直に「配列にまとめて書こう」として、数値を関数ポインタに変換したところ、こう言われました。
error[E0080]: evaluation of constant value failed
= note: the rules on what exactly is undefined behavior aren't clear,
so this check might be overzealous.
「整数を関数ポインタとして扱うのは未定義動作かもしれないので許可しない」という趣旨です。Cならキャスト一発で終わる場面です。
解決は、配列にまとめず2つの別々の変数として置き、リンカスクリプトで順番に並べるやり方でした。
#[link_section = ".vector_table.stack"] pub static INITIAL_SP: u32 = 0x2001_0000; // ただの数値として置く #[link_section = ".vector_table.reset"] pub static RESET_VECTOR: unsafe extern "C" fn() -> ! = _reset; // 関数のまま置く
.vector_table ORIGIN(FLASH) : {
KEEP(*(.vector_table.stack));
KEEP(*(.vector_table.reset));
} > FLASH
これはRustの性格がよく出た場面だと思いました。安全性のための検査が、ハードウェアを直接触る場面では「手間」として現れます。逃げ道(この場合はメモリ配置をリンカに任せる)は用意されていますが、Cの感覚のまま書くと必ずどこかで止められます。
裏を返せば、この「止められる」が、実行時のバグを事前に潰しているとも言えます。組み込みでRustを選ぶ意味は、まさにここにあります😊
①書けるか──もう書ける
ここからは調べた話です。まず①の層は、はっきり決着がついています。
| 要素 | 現状(2026年8月時点) |
|---|---|
| embedded-hal | 1.0が2024年1月にリリース済み。累計2,962万ダウンロード。チップごとの違いを吸収する共通インターフェースが固まった |
| Embassy | 非同期フレームワーク。今回使用。nRF・STM32・RP2040・ESPなど主要チップに対応 |
| Ariel OS | ★Rustで書かれた新しいRTOS。Embassyの上に構築され、マルチコアのプリエンプティブスケジューリングに対応(Rust製の組み込みOSでは初)。Cortex-M / RISC-V / Xtensa対応、Nordic・ST・Espressif・Raspberry Piのボードに対応。Apache-2.0 / MIT |
| Zephyr | C製の主要RTOSだが、Rustでアプリを書く公式サポートがある。Embassyも動く |
とくに embedded-hal 1.0 が効いています。「センサーのドライバを書いたら、別のマイコンでもそのまま使える」という状態が、2024年に土台として固まりました。ここが揺れている間は何を作っても作り直しでしたが、その時期は終わっています。
Ariel OS は今年FOSDEMで発表されたばかりの新顔です。日本語の解説がまだほとんど見当たりませんが、「Rustで書かれたRTOS」という選択肢が現実に存在するようになったこと自体が、①の層の決着を象徴していると思います。
★②認証を通せるか──2026年に動いた
ここが今回いちばんの発見でした。
組み込みの本丸(車載・医療・産業機器)では、ソフトが安全規格の認証を通らないと製品に載せられません。そして「Rustは認証が通らないから無理」というのが、長らく話の終着点でした。
その状況が変わっています。Ferrocene という認証済みのRustツールチェーンがあります。
| 規格 | 分野 | Ferroceneの対応 |
|---|---|---|
| ISO 26262 | 車載 | ツールチェーンがASIL D(最も厳しい等級)に対応 |
| IEC 61508 | 産業機器 | SIL 3 |
| IEC 62304 | 医療機器 | Class C |
| DO-178C | 航空 | DAL C に向けた対応 |
| ★coreライブラリ | — | 認証済みサブセットが ISO 26262 ASIL B / IEC 61508 SIL 2 |
注目したいのは最後の行です。これまで認証されていたのはコンパイラ(ツールチェーン)だけでした。それが標準ライブラリの中核部分にまで踏み込んだのが、この1年の動きです。
2026年2月のリリースでは、認証済みの関数が2,903個から5,169個へ増えました。core::slice、core::iter、core::ffi、core::result といった、実際に書けば必ず使う部分が入っています。
⚠ ただし、認証は無料ではありません
Ferroceneはオープンソース(MIT / Apache-2.0)ですが、認証の資格を伴って使うには有料です。個人向けで €25/月(年払い €240)、企業向けは個別見積もりになります。
「Rustのコンパイラは無料」と「認証済みのRustは有料」は別の話です。ここはCの商用コンパイラと同じ構図で、むしろ組み込み業界にとっては見慣れた形とも言えます。
③現場が乗り換えるか──ここが本丸
では③はどうか。Rust公式ブログが2026年1月に「安全critical領域に出すには何が要るか」という記事を出しています。そこで挙げられている「まだ足りないもの」が、そのまま現状の答えになっていました。
| 足りないもの | 意味するところ |
|---|---|
| ★AUTOSAR Classic / OSEK 互換のRTOSが無い | 車載ソフトの標準的な作りに乗れない。部品メーカーとの共通言語が使えない |
| MATLAB / Simulink との連携が限定的 | 制御系はモデルからコードを自動生成する文化。その出口がCのまま |
| no_std に数学関数が無い | sin・cos すら標準では使えない。制御では致命的になり得る |
| 認証のためのツールが未成熟 | Cには何十年ぶんの実績と道具がある |
| 依存クレートの扱い方の指針 | 重要度が上がるほど依存は減らしたいが、その作法がまだ確立していない |
| FFI(C言語との橋渡し)の指針 | ★ほとんどのチームは既存のCを書き直さない。共存の作法こそが要る |
最後の項目が、いちばん実態を表していると思います。公式が「大半のチームは既存システムを書き直さない」と前提を置いているのです。つまり目指されているのは置き換えではなく共存です。
先ほどの実測で「依存クレート54個」という数字が出ましたが、これも③に効いてきます。重要度の高い製品では、54個のクレートすべてを監査対象にできるのかという話になるからです。素のベアメタルなら依存ゼロですが、その代わり自分で書く量が増えます。ここでも同じトレードオフが顔を出します。
★目的別・どれを選ぶかの判断表
調べたことと測ったことを、目的別にまとめます。
| 目的・状況 | 向いている選択 | 強み | 弱み |
|---|---|---|---|
| フラッシュが数KBしかない /すべて把握したい |
①素のベアメタル | 依存ゼロ・134B・ビルド0.11秒。中身が全部見える | ベクタテーブルもリンカスクリプトも自分で書く(35行)。移植性は無い |
| ふつうのマイコン開発 (フラッシュ数十KB〜) |
②cortex-m-rt | いちばん短く書ける(14行)。定番なので情報が多い | 2.6KB・32クレート。中で何が起きているかは見えにくい |
| 割り込み・通信が多い /複数の処理が並行する |
③Embassy | 待ちを await で書ける。ステートマシンを手で書かずに済む |
3.1KB・54クレート・RAM 56B。小さいチップには載せにくい |
| OSの機能がほしい (ネットワーク・マルチコア) |
Ariel OS / Zephyr+Rust | スケジューラや通信が最初から付く。Ariel OSはマルチコア対応 | Ariel OSは新しく実績が浅い。日本語の情報がほぼ無い |
| 車載・医療・産業機器 (認証が要る) |
Ferrocene | ISO 26262 ASIL D 対応。coreの一部も ASIL B | 有料。AUTOSAR互換RTOSが無い。Simulink連携も弱い |
| 既存のC資産が大量にある | Cを続け、FFIで部分的にRust | 書き直しのリスクを負わずに、新規部分だけ安全性を上げられる | 橋渡しの作法がまだ確立途上。公式も課題として挙げている |
| 学習・試作 | ②+QEMU | ★実機なしで始められる。ターゲット追加は1秒、環境構築はほぼ不要 | 実機特有の問題(電源・タイミング)は分からない |
まとめ
- ✅ 「牙城は崩れるのか」は3層に分けると答えられる。①書けるか=もう崩れた/②認証=崩れかけている/③現場=当分崩れない
- ✅ ①はもう決着。embedded-hal 1.0(2024年1月)で土台が固まり、Embassy・Ariel OS・ZephyrのRust対応が揃った。★今回、3つの書き方すべてがQEMUで動いた
- ★ ②が2026年に動いた。Ferroceneがツールチェーンで ISO 26262 ASIL D、さらにcoreライブラリの認証済みサブセットが ASIL B に到達。認証済み関数は2,903→5,169個。⚠ただし認証付きの利用は有料
- ★ ③には具体的な穴が残っている。AUTOSAR Classic互換のRTOSが無い、Simulink連携が弱い、no_stdにsin/cosが無い。★公式自身が「大半のチームは書き直さない」と前提を置き、置き換えではなく共存を目指している
- ★ 実測でいちばん面白かったのは「短く書けるほど焼かれるサイズは大きくなる」。35行→14行で焼かれる量は約20倍(134B→2,636B)。Rustが重いのではなく、楽をしたぶん重い
- ⚠ つまずき:ベクタテーブルで整数を関数ポインタに変換できずコンパイラに止められた。Cならキャスト一発の場面。安全性のための検査が、ハードウェアを触る場面では手間として現れる
- ✅ 始めるのは驚くほど簡単。
rustup target add thumbv7m-none-eabiが1秒で終わり、あとはQEMUで動く。実機を買う前に試せる
調べる前は「まだ早いのでは」と思っていました。実際に手を動かしてみると、①の意味では完全に実用でした。1秒で環境が整い、134バイトのバイナリが動く。
一方で、③の壁は数字や機能では崩れません。AUTOSAR互換のRTOSが無いという1点だけでも、車載の現場では話が止まります。技術的な準備はほぼ整っていて、残っているのは「業界の作り」の問題——というのが、今回いちばん腑に落ちた結論でした😊
それでは、今回はここまで。最後までありがとうございました😊
※ 実測は MacBook Pro(Apple Silicon M4 / rustc 1.97.1)で、QEMU 11.0.2 の lm3s6965evb(Cortex-M3)に対して行いました。ビルド時間は cargo clean してから3回測った中央値、焼かれるサイズはELFのロード可能セグメントの合計です。★エコシステム・認証の情報は2026年8月時点の公開情報にもとづきます(Ferroceneの認証範囲、embedded-hal 1.0のリリース時期と累計ダウンロード数、Ariel OSの対応範囲、Rust公式ブログが挙げる課題)。認証の適用可否は製品・体制・審査機関によって変わるため、実際の採用にあたっては一次情報と個別の確認が必要です。