liveBook · Manning

Chapter 7: Files and storage

What you will learn in chapter 7 of Rust in Action: Systems programming concepts and techniques

The introduction explains why permanent data storage is more involved than keeping data in RAM, then previews the chapter’s projects.

After this chapter you will be able to

What it builds on

Section by section

Introduction

The introduction explains why permanent data storage is more involved than keeping data in RAM, then previews the chapter’s projects.

Key terms: RAM, storage media, software indirection

7.1 What is a file format?

The section defines file formats as ordered byte sequences and explains the trade-offs among different representations.

Key terms: file formats, ordered sequence of bytes, storage media, performance, human-readability, portability, self-describing

7.2 Creating your own file formats for data storage

The section contrasts recommended long-term database storage with common plain-text files and notes Rust’s support for on-disk formats.

Key terms: battle-tested database, plain text files, configuration files, human-readable, machine-readable, Rust ecosystem, on-disk formats

7.2.1 Writing data to disk with serde and the bincode format

This section introduces serde for converting a Rust struct into several storage formats and compares readable and compact encodings.

Key terms: serde, Serialize, Deserialize, derive, City struct, JSON, CBOR, bincode, human-readable, machine-readable

7.3 Implementing a hexdump clone

The section builds a small hexdump-style file viewer, moving from string input to a real file while explaining byte reads and command-line arguments.

Key terms: hexdump, fview, byte, hexadecimal, string literal, &[u8], std::io::prelude, std::io::Read, while let Ok(_), read_exact

7.4 File operations in Rust

The section shifts attention from byte sequences to files and introduces a discussion of more granular file operations.

7.4.1 Opening a file in Rust and controlling its file mode

This subsection explains what the OS provides through files and how Rust opens or creates them with different access modes.

Key terms: files, operating system (OS), permissions, std::fs::File, open(), create(), std::fs::OpenOptions, append mode, writeable, readable

7.4.2 Interacting with the filesystem in a type-safe manner with std::fs::Path

This section introduces Rust’s path types and explains why they are preferable to manipulating filesystem paths as strings.

Key terms: Path, PathBuf, from(), set_extension(), path separators, portability

7.5 Implementing a key-value store with a log-structured, append-only storage architecture

Introduces log-structured, append-only databases as a case study in resilient storage and fast reads.

Key terms: log-structured, append-only model, resilient, optimal read performance

7.5.1 The key-value model

This section defines actionkv’s key-value model and explains the kinds of queries it supports.

Key terms: key-value model, key, value, actionkv, [u8]

7.5.2 Introducing actionkv v1: An in-memory key-value store with a command-line interface

The section explains why actionkv starts as a library crate and how its Cargo configuration identifies an executable that uses the shared library.

Key terms: library crate, lib.rs, cargo new --lib actionkv, Cargo.toml, [[bin]], executable binaries

7.6 Actionkv v1: The front-end code

This section identifies actionkv v1’s public operations and explains how its command-line front end dispatches requests while delegating storage work.

Key terms: matching facilities, command-line arguments, ActionKV, store, get, delete, insert, update, std::collections::HashMap

7.6.1 Tailoring what is compiled with conditional compilation

This section explains how Rust uses cfg attributes to compile target-specific code and how to combine or supply cfg conditions.

Key terms: conditional compilation, cfg attributes, target, const USAGE, not, all, any, --cfg ATTRIBUTE

7.7 Understanding the core of actionkv: The libactionkv crate

This section identifies libactionkv::ActionKV as the component that carries out the command-line application's storage work.

Key terms: libactionkv::ActionKV, ActionKV struct, on-disk format

7.7.1 Initializing the ActionKV struct

This section explains how ActionKV opens its data file and rebuilds an in-memory index, introduces byte-oriented type aliases, and clarifies how a file read signals EOF.

Key terms: ActionKV::open(), ActionKV::load(), in-memory index, offsets, Result, ByteStr, ByteString, str, String, slices

7.7.2 Processing an individual record

The section explains how ActionKV reads a Bitcask record by using its fixed-size header to locate and check the key-value data.

Key terms: Bitcask, record, process_record(), checksum, key_len, val_len

7.7.3 Writing multi-byte binary data to disk in a guaranteed byte order

This section explains why portable binary storage needs a defined byte order and shows how the byteorder crate supplies one when writing and reading multi-byte values.

Key terms: multi-byte data, byte order, little endian, big endian, byteorder, std::io::Read, std::io::Write, ReadBytesExt, WriteBytesExt, LittleEndian

7.7.4 Validating I/O errors with checksums

Explains how checksums can detect corrupted disk data in actionkv, compares checksum choices, and illustrates parity checking.

Key terms: checksum, checking function, corrupted, parity bit, CRC32, cryptographic hash functions, parity checking, bitstream, ASCII

7.7.5 Inserting a new key-value pair into an existing database

This section explains how an append-only key-value store handles updates and deletions by appending records and letting later records supersede earlier ones during loading.

Key terms: append-only design, insert, update, delete, load(), stale data

7.7.6 The full code listing for actionkv

This subsection points readers to the full libactionkv code listing and marks the completion of a key-value store that stores and retrieves data.

7.7.7 Working with keys and values with HashMap and BTreeMap

The section distinguishes Rust’s key-value collections and explains how hashing enables lookup while collisions can slow it down.

Key terms: key-value pairs, associative array, hash table, HashMap, BTreeMap, hash function, hash value, constant time lookup, O(1), hash collision

7.7.8 Creating a HashMap and populating it with values

This subsection introduces populating a Rust HashMap and contrasts method calls with compile-time JSON literals.

Key terms: HashMap, literal syntax, method calls, serde-json crate, JSON string literals, compile time, runtime performance

7.7.9 Retrieving values from HashMap and BTreeMap

This section compares two ways to retrieve map values and identifies other HashMap operations and a reason to use BTreeMap.

Key terms: square brackets, &V, .get(), Option<&V>, .remove(), .keys(), .values(), .iter(), BTreeMap

7.7.10 How to decide between HashMap and BTreeMap

Gives a default rule for choosing between HashMap and BTreeMap and explains when BTreeMap may be preferable.

Key terms: HashMap, BTreeMap, natural ordering

7.7.11 Adding a database index to actionkv v2.0

The section explains how actionkv v2.0 persists its index to avoid rebuilding it on every run and uses that index to locate records on disk.

Key terms: actionkv v1, actionkv v2.0, index, get operation, INDEX_KEY, on-disk, in-memory

7.8 Summary

Concepts explained in this chapter

← Chapter 6: Memory Chapter 8: Networking →

These guides are generated from the text of Manning books by Manning's knowledge graph and link to the sections they draw on. The full books are available on liveBook and at manning.com.