rust-no-std — Claude Code skill for no_std Rust
A skill that walks your agent through no_std Rust: the core/alloc/std boundary, global allocators, panic handlers and host-side testing.
npx skills add mohitmishra786/low-level-dev-skills --skill rust-no-std -a claude-codeWhat rust-no-std does
The skill guides an agent through #![no_std] code — library crates for embedded and bare-metal targets that cannot link against std. It lays out what core offers without an OS or heap, how alloc differs, and how to opt into Vec, String and Box behind an alloc feature.
Inside you get crate structure examples, a comparison table of core vs alloc vs std, a global allocator built on linked-list-allocator, and several #[panic_handler] options: a spin loop, defmt with cortex_m::asm::udf, or ready-made crates such as panic-halt and panic-probe. Testing has its own section — #![cfg_attr(not(test), no_std)] keeps std available only in test mode, while cargo check --target thumbv7em-none-eabihf verifies compliance without hardware. For flashing with probe-rs and defmt logging the skill points to skills/embedded/embedded-rust, and for cross-compilation target setup to skills/rust/rust-cross.
Scope is the catch: the full embedded workflow and target configuration live in other skills, so an RTIC or probe-rs project needs them alongside this one. Tests on the embedded target itself won't run either — bare metal has no test runner, and the author suggests defmt-test or probe-run instead.
What it helps with
- write a parser crate that works without std
- use Vec and String in no_std via the alloc feature
- pick a global allocator for an embedded target
- choose a panic handler for debugging or production
- run no_std crate tests on the host machine
Example prompts for the agent
- help me write a no_std crate that parses bytes with borrowed slices
- how do I use Vec in no_std and which allocator should I add
- set up host testing for my no_std crate with cfg_attr
How to install rust-no-std
1Skills CLI (Vercel)
npx skills add mohitmishra786/low-level-dev-skills --skill rust-no-std -a claude-code2Manually: put the files in place
~/.claude/skills/rust-no-std/SKILL.mdCopy the whole skill folder; for one project use .claude/skills/rust-no-std/
1Skills CLI (Vercel)
npx skills add mohitmishra786/low-level-dev-skills --skill rust-no-std -a codex2Manually: put the files in place
.agents/skills/rust-no-std/SKILL.mdFor all projects use ~/.agents/skills/
1Skills CLI (Vercel)
npx skills add mohitmishra786/low-level-dev-skills --skill rust-no-std -a cursor2Manually: put the files in place
.cursor/skills/rust-no-std/SKILL.mdCursor also reads .agents/skills/ and .claude/skills/
1GitHub CLI
gh skill install mohitmishra786/low-level-dev-skills rust-no-std2Manually: put the files in place
.github/skills/rust-no-std/SKILL.md1Skills CLI (Vercel)
npx skills add mohitmishra786/low-level-dev-skills --skill rust-no-std -a gemini-cli2Platform CLI
gemini skills install https://github.com/mohitmishra786/low-level-dev-skills --path skills/rust/rust-no-stdManually: put the files in place
.devin/skills/rust-no-std/SKILL.mdThe legacy .windsurf/skills/ path still works
Skills CLI (Vercel)
npx skills add mohitmishra786/low-level-dev-skills --skill rust-no-std -a opencodeClaude.ai
Zip the folder and upload it in Settings → Capabilities → Skills
Step by step, with commands for each platform: How to install skills in Claude Code: five ways
Questions about rust-no-std
How do I install rust-no-std in Claude?
Run npx skills add mohitmishra786/low-level-dev-skills --skill rust-no-std -a claude-code. The skill lands in ~/.claude/skills/rust-no-std/SKILL.md.
What does rust-no-std do?
It guides an agent through no_std Rust development: #![no_std] crate structure, the difference between core, alloc and std, custom global allocators, panic handler selection and host-side testing.
Can I test a no_std crate without hardware?
Yes. #![cfg_attr(not(test), no_std)] allows std during tests, and cargo check --target thumbv7em-none-eabihf verifies the build for an embedded target without running on it.
Source text
SKILL.md · MIT
---
name: rust-no-std
description: "Guide no_std Rust development for embedded and bare-metal targets. Use when writing #![no_std] crates, understanding core vs alloc vs std, implementing custom global allocators, selecting panic handlers for embedded, or testing no_std crates on the host without hardware."
user-invocable: true
triggers:
- write a no_std Rust crate
- use Vec or String without std in Rust
- implement a global allocator in Rust
- handle panics in no_std embedded Rust
- test a no_std crate on the host
- set up no_std with the alloc feature
- difference between core alloc and std in Rust
---
# Rust no_std
## Purpose
Guide agents through `#![no_std]` Rust development: what `core` and `alloc` provide vs `std`, implementing custom global allocators, panic handler selection for embedded targets, and strategies for testing `no_std` crates on the host machine.
## When to Use
Use this skill when writing or debugging `#![no_std]` Rust code — library crates for embedded targets, or bare-metal firmware that cannot link against `std`. For the full embedded development workflow (probe-rs flashing, defmt logging, RTIC), use `skills/embedded/embedded-rust`. For cross-compilation target setup, use `skills/rust/rust-cross`. This skill focuses specifically on the `no_std` / `core` / `alloc` boundary and panic handler selection.
## Examples
- "I need a parser crate that works without std" → structure with `#![no_std]`, feature-gate alloc APIs, use borrowed slices for core API
- "How do I use Vec in a no_std environment?" → add `alloc` feature, provide a global allocator (e.g., `linked-list-allocator`), use `alloc::vec::Vec`
- "How do I test my no_std crate on my laptop?" → use `#![cfg_attr(not(test), no_std)]` to allow std in test mode, or `cargo test --target x86_64-unknown-linux-gnu`
## Workflow
### 1. no_std crate structure
```rust
// 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
}
```
```toml
# Cargo.toml
[features]
default = []
alloc = [] # opt-in to heap allocation
[dependencies]
# no_std-compatible dependencies only
```
### 2. core vs alloc vs std
| Crate | Requires OS | Requires heap | Provides |
|-------|------------|--------------|---------|
| `core` | No | No | Primitives, traits, iter, fmt, mem, ptr, slice, option, result |
| `alloc` | No | Yes (allocator) | Vec, String, Box, Arc, Rc, HashMap (requires global allocator) |
| `std` | Yes | Yes | All of core + alloc + OS APIs (threads, files, sockets, env) |
`std` re-exports everything in `core` and `alloc`, so `use std::fmt` and `use core::fmt` are equivalent when `std` is available.
What's available in `core` only (no heap, no OS):
```rust
// 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. Custom global allocator
To use `alloc` crate in `no_std`, provide a global allocator:
```rust
// 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);
}
}
```
```toml
[dependencies]
linked-list-allocator = { version = "0.10", default-features = false }
```
```rust
// 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);
```
Common embedded allocator crates:
- `linked-list-allocator`: general purpose, `no_std`
- `buddy-alloc`: power-of-two buddy system
- `dlmalloc`: port of Doug Lea's malloc
- `talc`: fast, suited for embedded
### 4. Panic handler
In `no_std`, you must provide a panic handler — Rust requires one:
```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. Writing portable no_std libraries
Design your library to work with and without `alloc`:
```rust
#![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. Testing no_std on host
```toml
# Cargo.toml
[dev-dependencies]
std = [] # allow std in tests only (via cfg)
[features]
std = []
```
```rust
// lib.rs
#![cfg_attr(not(test), no_std)] // no_std except during tests
// Tests compile normally with std — only library code is no_std
```
Or use a separate test harness:
```bash
# 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
```
```bash
# Check no_std compliance without hardware
cargo check --target thumbv7em-none-eabihf
cargo build --target thumbv7em-none-eabihf
```
## Related skills
- Use `skills/embedded/embedded-rust` for probe-rs, defmt, and RTIC with no_std
- Use `skills/rust/rust-cross` for cross-compilation target setup
- Use `skills/rust/rust-unsafe` for unsafe patterns needed in allocator implementations
- Use `skills/embedded/linker-scripts` for heap region placement in bare-metal targets
© mohitmishra786 · MIT · mohitmishra786/low-level-dev-skills