关于 rust-no-std
面向开发或调试 `#![no_std]` 库和裸机固件的 Rust 开发者,说明 `core` 与 `alloc` 的边界、分配器和 panic 处理程序设置,以及主机端测试。
主要功能
- 01比较 core、alloc 和 std
- 02设置全局分配器
- 03选择 no_std panic 处理程序
- 04设计支持可选 alloc 的库
- 05在主机上测试 no_std crate
适用场景
- 01构建不依赖 std 的解析器
- 02在 no_std 环境中使用 Vec
- 03在笔记本上测试 no_std 库
- 04为嵌入式目标检查代码
安装
复制下面的命令,在终端中运行。
$
npx skills add https:/ / github.com/ mohitmishra786/ low-level-dev-skills --skill rust-no-std安装后在 Claude Code 中输入/rust-no-std确认已加载。
详细介绍
Rust no_std
用途
指导 Agent 进行 #![no_std] Rust 开发:了解 core 和 alloc 相较于 std 提供的功能、实现自定义全局分配器、为嵌入式目标选择 panic 处理程序,以及在主机上测试 no_std crate 的策略。
适用场景
编写或调试 #![no_std] Rust 代码时使用此技能,包括面向嵌入式目标的库 crate,或无法链接 std 的裸机固件。关于完整的嵌入式开发工作流(probe-rs 刷写、defmt 日志记录、RTIC),请使用 skills/embedded/embedded-rust。关于交叉编译目标设置,请使用 skills/rust/rust-cross。此技能专门介绍 no_std / core / alloc 的边界以及 panic 处理程序的选择。
示例
- “我需要一个不依赖 std 的解析器 crate” → 使用
#![no_std]构建,将 alloc API 设为可选功能,核心 API 使用借用切片 - “如何在 no_std 环境中使用 Vec?” → 添加
alloc功能,提供全局分配器(例如linked-list-allocator),使用alloc::vec::Vec - “如何在笔记本电脑上测试 no_std crate?” → 使用
#![cfg_attr(not(test), no_std)],以便在测试模式下启用 std;或者使用cargo test --target x86_64-unknown-linux-gnu
工作流
1. no_std crate 结构
// src/lib.rs
#![no_std]
// core is always available (no OS needed)
use core::fmt;
use core::mem;
use core::slice;
// alloc: heap collections — requires a global allocator
#[cfg(feature = "alloc")]
extern crate alloc;
#[cfg(feature = "alloc")]
use alloc::{vec::Vec, string::String, boxed::Box, format};
pub fn add(a: u32, b: u32) -> u32 {
a + b
}
# Cargo.toml
[features]
default = []
alloc = [] # opt-in to heap allocation
[dependencies]
# no_std-compatible dependencies only
2. core、alloc 与 std 的区别
| Crate | 需要操作系统 | 需要堆 | 提供内容 |
|---|---|---|---|
core | 否 | 否 | 基本类型、trait、迭代器、fmt、mem、ptr、slice、option、result |
alloc | 否 | 是(分配器) | Vec、String、Box、Arc、Rc、HashMap(需要全局分配器) |
std | 是 | 是 | core + alloc 的全部内容,以及操作系统 API(线程、文件、套接字、环境变量) |
std 重新导出 core 和 alloc 中的所有内容,因此在 std 可用时,use std::fmt 和 use core::fmt 等效。
仅在 core 中可用的内容(无堆、无操作系统):
// These work in no_std:
core::fmt::Write // trait for write! macro
core::iter // iterators
core::ops // operators (+, -, *, Deref, etc.)
core::option::Option
core::result::Result
core::mem::{size_of, align_of, swap, replace}
core::ptr::{read, write, null, NonNull}
core::slice, core::str
core::sync::atomic // atomic types
core::cell::{Cell, UnsafeCell, RefCell}
core::cmp, core::convert, core::clone, core::default
core::num // numeric conversions
core::panic::PanicInfo // for panic handler
3. 自定义全局分配器
要在 no_std 中使用 alloc crate,需提供全局分配器:
// src/allocator.rs — embedded allocator using linked_list_allocator
use linked_list_allocator::LockedHeap;
#[global_allocator]
static ALLOCATOR: LockedHeap = LockedHeap::empty();
pub fn init_heap(heap_start: usize, heap_size: usize) {
unsafe {
ALLOCATOR.lock().init(heap_start as *mut u8, heap_size);
}
}
[dependencies]
linked-list-allocator = { version = "0.10", default-features = false }
// src/main.rs (bare-metal)
#![no_std]
#![no_main]
extern crate alloc;
use alloc::vec::Vec;
mod allocator;
// In init code (after BSS/data init):
allocator::init_heap(0x20010000, 0x10000); // 64KB heap at RAM+64KB
// Now alloc types work:
let mut v: Vec<u32> = Vec::new();
v.push(42);
常见的嵌入式分配器 crate:
linked-list-allocator:通用型,支持no_stdbuddy-alloc:二的幂次伙伴系统dlmalloc:Doug Lea 的 malloc 移植版本talc:速度快,适用于嵌入式
4. Panic 处理程序
在 no_std 中,必须提供 panic 处理程序——这是 Rust 的要求:
// Option 1: halt on panic (simplest, production)
use core::panic::PanicInfo;
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
loop {} // spin forever
}
// Option 2: print panic info via defmt (embedded with debug probe)
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
defmt::error!("{}", defmt::Display2Format(info));
cortex_m::asm::udf(); // undefined instruction → hard fault
}
// Option 3: use a panic crate (in Cargo.toml)
// panic-halt = "0.2" — spin loop
// panic-reset = "0.1.1" — reset MCU
// panic-probe = "0.3" — defmt + probe-rs
5. 编写可移植的 no_std 库
设计库,使其兼容有无 alloc 的环境:
#![no_std]
#[cfg(feature = "alloc")]
extern crate alloc;
pub struct Parser<'a> {
data: &'a [u8], // borrowed slice: no allocation needed
pos: usize,
}
impl<'a> Parser<'a> {
pub fn new(data: &'a [u8]) -> Self {
Parser { data, pos: 0 }
}
// Core API: return borrowed data, no allocation
pub fn next_token(&mut self) -> Option<&'a [u8]> { /* ... */ None }
// Alloc API: only when alloc feature is enabled
#[cfg(feature = "alloc")]
pub fn collect_all(&mut self) -> alloc::vec::Vec<&'a [u8]> {
let mut tokens = alloc::vec::Vec::new();
while let Some(tok) = self.next_token() {
tokens.push(tok);
}
tokens
}
}
6. 在主机上测试 no_std
# Cargo.toml
[dev-dependencies]
std = [] # allow std in tests only (via cfg)
[features]
std = []
// lib.rs
#![cfg_attr(not(test), no_std)] // no_std except during tests
// Tests compile normally with std — only library code is no_std
也可以使用独立的测试框架:
# Run tests targeting the host (std available for test framework)
cargo test --target x86_64-unknown-linux-gnu
# Test with the actual embedded target using QEMU
cargo test --target thumbv7em-none-eabihf # fails: no test runner on bare metal
# Solution: use defmt-test or probe-run for on-target testing
# Or: architecture-neutral pure logic tests on host
# Check no_std compliance without hardware
cargo check --target thumbv7em-none-eabihf
cargo build --target thumbv7em-none-eabihf
相关技能
- 使用
skills/embedded/embedded-rust,了解在 no_std 中使用 probe-rs、defmt 和 RTIC - 使用
skills/rust/rust-cross设置交叉编译目标 - 使用
skills/rust/rust-unsafe了解分配器实现所需的不安全模式 - 使用
skills/embedded/linker-scripts设置裸机目标中的堆区域位置
评论
支持 Markdown