Rust no_std 裸机移植:9 条避坑与实战手册

引言:从"云端"到"铁端"

Rust 的野心从来不是只做 C++ 的替代品,它的目标是全场景覆盖 。但从 OS 环境切换到"裸机"(Bare Metal),就像是从五星级酒店搬到了荒岛求生。没有操作系统,没有文件系统,甚至连 std 库都没了。

怎么优雅地完成代码移植?收好这 9 条"求生指南"。


第一阶段:生存环境大检查

规则 1:先过 WASM 这道关

如果你想让代码跑在单片机上,先试试能不能跑在浏览器里(WASM)。

  • 技巧 :使用 wasm32-wasip1 或 wasm32-unknown-unknown 进行测试。如果你的代码在 WASM 下都因为找不到某些 std 方法而挂掉,那单片机基本也悬了。
规则 2:利用 cargo tree 捕获隐藏的"间谍"

很多时候你的代码没用 std,但你引用的依赖项偷偷用了。

  • 必杀技:

    cargo tree --edges no-dev --format "{p} {f}"

  • 这个命令能让你看清哪个依赖项开启了 std feature。一旦发现,立刻在 Cargo.toml 里加上 default-features = false。


第二阶段:代码重构------从 std 到 core

规则 3:全员换装 core 和 alloc

在 lib.rs 开头大声宣布:

rust 复制代码
#![no_std]
extern crate alloc;
  • 骚操作 :全局搜索 std:: 改为 core::。比如 std::cmp::max 变成 core::cmp::max。如果涉及到内存分配(如 Vec, BTreeMap),请指向 alloc::。
规则4:让 std 变成"可选配置"

为了让你的库既能跑在 Linux 服务器上,又能跑在单片机上,利用 Cargo Features:

rust 复制代码
[features]
default = ["std"]
std = ["itertools/use_std", "num-traits/std"]

在代码里通过 #[cfg(feature = "std")] 来包裹那些只有 OS 才能跑的功能(比如文件 I/O)。


第三阶段:测试------解决"嵌入式开发最难的一环"

规则 5:清楚 cargo test 永远带 std

这是一个很多新手都会掉进去的坑:即便你标记了 no_std ,运行 cargo test 时,Rust 依然会链接标准库来运行测试框架。 这意味着:cargo test 通过了,不代表你的 no_std 环境没问题。

规则 6:QEMU 仍旧权威!

既然本地 test 不靠谱,那就上模拟器。

  1. 安装 qemu-system-arm。

  2. 创建一个独立的 tests/embedded 子项目。

  3. 利用 semihosting(半主机)技术,让单片机模拟器把日志打印到你电脑的控制台上。

  • 代码示范:
rust 复制代码
#[entry]
fn main() -> ! {
    // ... 初始化堆内存 ...
    hprintln!("Running embedded tests...");
    if test_logic() {
        debug::exit(debug::EXIT_SUCCESS);
    } else {
        debug::exit(debug::EXIT_FAILURE);
    }
    loop {}
}

第四阶段:进阶与工程化

规则 7:贴上"嵌入式友好"标签

如果你打算开源你的库,记得在 Cargo.toml 里加上 categories = ["no-std", "embedded"]。这不仅是荣誉勋章,更是流量密码,让全世界的嵌入式大佬能搜到你。

规则 8:极致压榨------拥抱 heapless(可选)

如果你的单片机内存小到连 alloc 都不敢开(比如只有几 KB RAM),那就祭出 heapless 库。

  • 逻辑 :用静态分配的 Vec、String 代替动态分配。

  • 代价 :你必须在编译时确定最大长度。比如 heapless::Vec<u8, 64>。

规则 9:CI 是唯一的真相

"在我的 QEMU 上能跑"不叫能跑,"在 GitHub Actions 里能跑"才叫稳。 在 CI 配置文件里加上 thumbv7m-none-eabi 目标的编译检查和 QEMU 自动化测试。只有自动化,才能防止你某天手抖引入了一个带 std 的库导致版本崩盘。


总结

移植 no_std 并不是简单的代码搬运,它其实是对模块化思维的终极考验。

  • 底层逻辑:把业务逻辑(Pure Logic)和平台实现(I/O, Time, File)彻底解耦。

  • 避坑指南 :很多三方库(如 thiserror)默认是不支持 no_std 的,寻找替代品(如 anyhow 的某些配置或自己写宏)是常态。

正如 Carl 在博文中展示的,哪怕是复杂的 range-set-blaze 这种涉及数据结构合并的算法,只要遵循这 9 条规则,也能在 Raspberry Pi Pico 的 LED 动画里闪耀。

嵌入式的世界没有银弹,但 Rust 给了你一把最锋利的刺刀。


参考:

相关推荐
小羊没烦恼!4 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
伞伞悦读4 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
C语言小火车4 天前
C/C++ 为什么需要编译器?
开发语言·c++
霍霍的袁4 天前
【C++】map 和 set 的使用 | 从用法到底层
开发语言·c++·学习·visual studio
孙启超4 天前
【AI开发之Rust】第 11 课:智能指针与内部可变性
开发语言·后端·rust
此生决int4 天前
深入理解C++系列(20)——C++11(下)
开发语言·c++
mikuyyds4 天前
geo-toolbox 水文插件算法解析:从 Muskingum 到 Muskingum-Cunge
算法·rust·gis
PC2005-cloud4 天前
Rust学习笔记:模块系统——mod、use、pub与多文件项目
rust
CoderYanger4 天前
A.每日一题:835. 图像重叠
java·开发语言·程序人生·leetcode·面试·职场和发展·学习方法
Amos_Web4 天前
Rspack 源码解析(十二):JavaScript Chunk 是如何被渲染出来的
前端·rust·源码