CodeBlanks Logo Icon CodeBlanks

Summary - no_std and embedded

Combine no_std, alloc and panic_handler to write an embedded skeleton.

no_std and embedded summary

Layers: core (everywhere), alloc (if there's an allocator), std (if there's an operating system). #![no_std] libraries can be used in any environment; binaries additionally need a #[panic_handler] and an entry point. In the embedded world drivers are written against embedded-hal traits and tested with mock hardware. For WebAssembly, the wasm32 targets and wasm-bindgen. For formatting without a heap, fixed-capacity buffers implementing core::fmt::Write are used.

A fixed-capacity buffer

fmt::Write needs a single method: write_str. Returning an error on a write that doesn't fit reports the overflow safely:

use core::fmt;

struct Tampon {
    veri: [u8; 32],
    uzunluk: usize,
}

impl fmt::Write for Tampon {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        let b = s.as_bytes();
        let son = self.uzunluk + b.len();
        if son > self.veri.len() {
            return Err(fmt::Error);
        }
        self.veri[self.uzunluk..son].copy_from_slice(b);
        self.uzunluk = son;
        Ok(())
    }
}

Practice Heap-free formatting

Write the Tampon type above and add fn as_str(&self) -> &str (core::str::from_utf8). Write with write!(t, "x={}", 42) and print the text; then try writing a 40-character text and print whether an error was returned, separated by a space.

Starter code

main.rs
use core::fmt::{self, Write};

fn main() {
    // buraya yaz
}

Sample solution

main.rs
use core::fmt::{self, Write};

struct Tampon {
    veri: [u8; 32],
    uzunluk: usize,
}

impl Tampon {
    fn as_str(&self) -> &str {
        core::str::from_utf8(&self.veri[..self.uzunluk]).unwrap_or("")
    }
}

impl fmt::Write for Tampon {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        let b = s.as_bytes();
        let son = self.uzunluk + b.len();
        if son > self.veri.len() {
            return Err(fmt::Error);
        }
        self.veri[self.uzunluk..son].copy_from_slice(b);
        self.uzunluk = son;
        Ok(())
    }
}

fn main() {
    let mut t = Tampon { veri: [0; 32], uzunluk: 0 };
    write!(t, "x={}", 42).unwrap();
    let metin = t.as_str().to_string();
    let tasti = t.write_str(&"a".repeat(40)).is_err();
    println!("{} {}", metin, tasti);
}

Expected output

x=42 true

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 How is formatting text with <code>write!</code> possible without a heap?

  • With println!
  • By writing into a fixed-size buffer implementing core::fmt::Write Correct answer
  • It isn't possible
  • With a String

Explanation: The heapless crate offers this pattern with ready types like String<N> and Vec<T, N>.

2 Copy the bytes

Question
self.veri[self.uzunluk..son].____(b);
Answer
self.veri[self.uzunluk..son].copy_from_slice(b);

Explanation: copy_from_slice copies the bytes of a slice of the same length.

3 Group summary

Term Meaning
#![no_std] Only core
extern crate alloc Heap types
#[panic_handler] Your own panic handler
fmt::Write Heap-free formatting

All lessons in Expert