Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
mohitmishra786 avatar

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-std

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs265
repo stars155
Last updatedJune 27, 2026
Repositorymohitmishra786/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

SKILL.mdMarkdownGitHub ↗

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

CrateRequires OSRequires heapProvides
coreNoNoPrimitives, traits, iter, fmt, mem, ptr, slice, option, result
allocNoYes (allocator)Vec, String, Box, Arc, Rc, HashMap (requires global allocator)
stdYesYesAll 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 handler

3. 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-rs

5. 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_std

Or 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-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

Related skills

Rustbackend

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.