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

Ownership and Borrowing

The most Rust-specific APIs make lifetime, sharing, and cleanup policy visible in ordinary values.

BytesTempfileOwnership contracts

Bytes: cheap shared storage with value-like operations

Consumer goal. Split a packet into immutable views while retaining another handle to the whole allocation.

use bytes::Bytes;

fn main() {
    let packet = Bytes::from_static(b"HEADpayload");

    let cloned = packet.clone();
    let header = packet.slice(..4);
    let body = packet.slice(4..);

    println!("header: {:?}", header);
    println!("body:   {:?}", body);
    println!("clone still sees {} bytes", cloned.len());
}

Observed stdout.

header: b"HEAD"
body:   b"payload"
clone still sees 11 bytes

Boundary behavior. An out-of-bounds slice panics, like indexing a standard slice. slice_ref and checked range logic are alternatives when the range is not trusted.

Compile-time guarantee. Each Bytes handle owns a claim on shared storage. The views remain valid even if the original packet variable is dropped.

Runtime boundary. The type cannot prove an arbitrary numeric range is in bounds. Mutation requires a different representation and stronger uniqueness conditions.

Tradeoff. Cheap clones are not free, and the value-like surface can hide atomic reference counting or retained backing allocations.

Deep-dive question. How can one concrete type represent static bytes, uniquely owned buffers, and reference-counted slices behind the same API?

Tempfile: cleanup follows the owner

Consumer goal. Use scratch storage that is removed on every return path without writing a cleanup protocol.

use std::io::{Read, Seek, SeekFrom, Write};

fn main() -> std::io::Result<()> {
    let mut file = tempfile::tempfile()?;
    file.write_all(b"temporary data")?;
    file.seek(SeekFrom::Start(0))?;

    let mut contents = String::new();
    file.read_to_string(&mut contents)?;
    println!("{contents}");

    // Dropping `file` closes and removes it.
    Ok(())
}

Observed stdout.

temporary data

Boundary behavior. File creation and I/O remain fallible. Cleanup occurs on drop, but operating-system failures during destructor cleanup cannot be reported through this function’s return value.

Compile-time guarantee. The handle cannot be used after it is moved or dropped. Early ? returns still run destructors for initialized values.

Runtime boundary. Filesystem permissions, capacity, and deletion semantics depend on the operating system.

Tradeoff. RAII makes the safe default effortless, but consumers needing to observe or recover from cleanup failure require an explicit close/persist API.

Deep-dive question. Which resource states are represented by distinct tempfile types, and when does an operation transfer cleanup responsibility?