
Rust No Std
- 265 installs
- 155 repo stars
- Updated June 27, 2026
- mohitmishra786/low-level-dev-skills
Develop bare-metal and embedded Rust firmware without std, using core, alloc, custom panic handlers, and hardware-specific HAL crates on microcontrollers.
About
Covers Rust no_std firmware: disabling std, supplying panic and alloc handlers, using embedded-hal traits, configuring target JSON specs, and linking with custom memory layouts. Enables agents to scaffold microcontroller crates, choose nightly features judiciously, and avoid std-only APIs in IRQ-critical paths.
- #![no_std] and panic handler setup
- core versus alloc crate boundaries
- Embedded-hal trait patterns
- Target spec and linker integration
- Interrupt-safe static mut alternatives
Rust No Std by the numbers
- 265 all-time installs (skills.sh)
- +23 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #45 of 121 Rust skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/mohitmishra786/low-level-dev-skills --skill rust-no-stdAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 265 |
|---|---|
| repo stars | ★ 155 |
| Last updated | June 27, 2026 |
| Repository | mohitmishra786/low-level-dev-skills ↗ |
What it does
Develop bare-metal and embedded Rust firmware without std, using core, alloc, custom panic handlers, and hardware-specific HAL crates on microcontrollers.
Files
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
allocfeature, provide a global allocator (e.g.,linked-list-allocator), usealloc::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, orcargo 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 only2. 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_stdbuddy-alloc: power-of-two buddy systemdlmalloc: port of Doug Lea's malloctalc: 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.next_token() {
tokens.push(tok);
}
tokens
}
}6. Testing no_std on host
# 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_stdOr use a separate test harness:
# 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-eabihfRelated skills
- Use
skills/embedded/embedded-rustfor probe-rs, defmt, and RTIC with no_std - Use
skills/rust/rust-crossfor cross-compilation target setup - Use
skills/rust/rust-unsafefor unsafe patterns needed in allocator implementations - Use
skills/embedded/linker-scriptsfor heap region placement in bare-metal targets