/rust-no-std
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.
$ npx -y skills add mohitmishra786/low-level-dev-skills --skill rust-no-std --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/rust-no-std
Context preview
The summary Claude sees to decide when to auto-load this skill.
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.
SKILL.md
rust-no-std.SKILL.mdname: 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
// 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 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):
// 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 handler3. Custom global allocator
To use `alloc` crate in `no_std`, provide a global allocator:
// 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);
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:
// 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-rs5. Writing portable no_std libraries
Design your library to work with and without `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.nRead more
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
// 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 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):
// 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 handler3. Custom global allocator
To use `alloc` crate in `no_std`, provide a global allocator:
// 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);
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:
// 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-rs5. Writing portable no_std libraries
Design your library to work with and without `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.nA curated suite of AI agent skills for systems and low-level programming — C/C++, Rust, Zig, GPU, bare-metal firmware, Linux kernel/driver development, computer architecture, compiler internals, HPC, and more.
Repo: mohitmishra786/low-level-dev-skills
Other skills on low-level-dev-skills.
- /custom-allocators
Custom allocator skill for memory allocation strategies. Use when implementing pool/slab/arena allocators, tuning jemalloc/mimalloc, writing Rust GlobalAlloc, or benchmarking allocator performance. Activates on queries about jemalloc, mimalloc, tcmalloc, arena allocator,
Open skill - /numa-programming
NUMA programming skill for multi-socket memory locality. Use when detecting NUMA topology, binding processes with numactl, using libnuma API, building NUMA-aware data structures, or measuring remote access penalties. Activates on queries about numactl, libnuma, NUMA topology,
Open skill - /af-xdp
AF_XDP skill for high-performance XDP sockets. Use when creating AF_XDP sockets, configuring UMEM and XSK rings, XDP_REDIRECT programs, copy vs zero-copy mode, or comparing with DPDK. Activates on queries about AF_XDP, xsk_umem, XDP_REDIRECT, libbpf xsk, or zero-copy XDP.
Open skill - /dpdk
DPDK skill for userspace packet I/O. Use when initializing EAL, configuring PMD drivers, using mbuf pools and rte_ring, setting up huge pages, RSS, or testpmd validation. Activates on queries about DPDK, EAL, rte_eth_rx_burst, hugepages, PMD, or testpmd.
Open skill - /io-uring
io_uring skill for Linux async I/O. Use when building high-performance servers with liburing, multi-shot operations, provided buffers, fixed files, zero-copy send, or tokio-uring. Activates on queries about io_uring, SQE/CQE, liburing, IORING_OP_PROVIDE_BUFFERS, or io_uring vs
Open skill - /adc-dac-baremetal
Bare-metal ADC and DAC skill. Use when configuring analog sampling, DMA-driven ADC, calibration, or DAC output on MCUs. Activates on queries about ADC bare-metal, sampling time, DMA ADC, or DAC channel setup.
Open skill

