Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Print Macros

Three macros for easy printing, mirroring the standard library API:

  • print!(...) — Print without newline
  • println!(...) — Print with newline
  • printk!(...) — Kernel print (alias for print!, named after the Linux kernel function)

How They Work

The Print Function

#![allow(unused)]
fn main() {
pub fn _print(args: fmt::Arguments) {
    use core::fmt::Write;
    unsafe {
        let writer = &mut *core::ptr::addr_of_mut!(WRITER);
        writer.write_fmt(args).unwrap();
    }
}
}

fmt::Arguments is the type produced by format_args!(). It’s a lazily-evaluated format string — nothing is formatted until write_fmt is called. This keeps the hot path efficient.

impl fmt::Write for Writer is what makes this work — it requires a single method:

#![allow(unused)]
fn main() {
impl fmt::Write for Writer {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        self.write_string(s);
        Ok(())
    }
}
}

The Macros

#![allow(unused)]
fn main() {
#[macro_export]
macro_rules! print {
    ($($arg:tt)*) => ($crate::vga::_print(format_args!($($arg)*)));
}

#[macro_export]
macro_rules! println {
    () => ($crate::print!("\n"));
    ($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
}

#[macro_export]
macro_rules! printk {
    ($($arg:tt)*) => ($crate::print!($($arg)*));
}
}

#[macro_export] makes the macros available at the crate root, so any module can use them without importing.

The $($arg:tt)* pattern captures any token tree — it’s the standard Rust pattern for variadic macro arguments that get passed to format_args!.

Usage

#![allow(unused)]
fn main() {
println!("KFS_1 - Kernel From Scratch");
println!("Stack pointer: 0x{:08x}", esp);
print!("Loading");
printk!(".");   // Kernel-style progress dots
}

The #[macro_use] Pattern

In lib.rs, the VGA module is declared with #[macro_use]:

#![allow(unused)]
fn main() {
#[macro_use]
mod vga;
}

This makes all #[macro_export] macros from vga available throughout the crate without needing use statements. Since print! and println! are used everywhere, this saves a lot of boilerplate.