p9r
p9r
// open source bench

p9r.dev

a workshop log. tutorials, snippets, libraries — straight from the wall.

est. 2026 / issue 09 / edition spring
turn the page
In this issue
  1. 01Setup ritual04 min
  2. 02Concurrency primer07 min
  3. 03Shipping the bundle06 min
  4. 04Library galleryscroll
Tutorial · 01

Setup Ritual

Before the first line of code, the wall must be primed. Every workshop begins with the same five minutes — a deliberate act that transforms a blank machine into a place where work happens.

Pin your toolchain. Configure the editor. Stand up the project skeleton in a single command, every time, the same way. Boredom is a feature here. Surprises during setup become bugs you ship later.

  • + lock toolchain (rust-toolchain.toml)
  • + editor config + format on save
  • + pre-commit hooks (lint + test)
  • + CI green on first push
setup.sh
# prime the wall — once per project
set -euo pipefail

cargo new --lib p9r
cd p9r

echo '[toolchain]' > rust-toolchain.toml
echo 'channel = "stable"' >> rust-toolchain.toml

cargo add anyhow tracing
cargo add --dev pretty_assertions

git init && git add . && \
    git commit -m "first tag on the wall"
Fig. 01 Repeatable bring-up. Same five lines, every project, no exceptions.
Tutorial · 02

Concurrency Primer

Threads are spray cans. Each one paints in parallel; if they collide on the same pixel, the wall smears. The job of the developer is to choreograph that paint without tying the cans together.

Channels over locks when you can. Bounded queues over unbounded ones, always. Cancellation as a first-class concern, never a postscript. The patterns below show three primitives that compose into almost any concurrent system.

  • + mpsc channels for fan-in
  • + broadcast for fan-out
  • + CancellationToken for graceful exit
workers.rs
use tokio::sync::{mpsc, broadcast};
use tokio_util::sync::CancellationToken;

pub async fn spawn_crew(
    n: usize,
    cancel: CancellationToken,
) -> mpsc::Sender<Job> {
    let (tx, mut rx) = mpsc::channel(256);

    for id in 0..n {
        let cancel = cancel.clone();
        tokio::spawn(async move {
            loop {
                tokio::select! {
                    _ = cancel.cancelled() => break,
                    Some(job) = rx.recv() => {
                        job.run(id).await;
                    }
                }
            }
        });
    }
    tx
}
Fig. 02 A crew of N workers, joined by a channel, dismissed by a token.
Tutorial · 03

Shipping the Bundle

A library is not done when it compiles. It is done when a stranger can install it, run an example, and read the changelog without writing to you. The seams between local craft and public art are the docs.

Versions are promises. The README is the gallery placard. The changelog is the receipt. Treat them as production code — diff them in PRs, fail CI when they drift from reality.

  • + semver, with a discipline
  • + README example that compiles in CI
  • + CHANGELOG.md per release
release.yml
name: release
on:
  push:
    tags: ['v*']

jobs:
  publish:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: verify changelog
        run: ./scripts/check-changelog.sh
      - name: test readme example
        run: cargo test --doc
      - name: publish
        run: cargo publish
        env:
          CARGO_REGISTRY_TOKEN: ${{ secrets.CRATES_IO }}
Fig. 03 Tag push, README compiles, changelog enforced. The receipt writes itself.
Workshop notes

Marginalia

No. 01

Tests run. The repo is the studio. Keep both clean enough to invite a stranger in.

No. 02

Pin the dependencies you trust. Replace the ones you don't. Write the ones nobody else has.

No. 03

A good error message is a small mural — it tells the reader exactly where the wall ends.

No. 04

Ship a crate the size of a sticker. Compose it with others. Avoid the temptation of frameworks.