Rust
Compile-time memory and concurrency safety, at the cost of a real learning investment.
The borrow checker prevents use-after-free and data races at compile time — not at runtime, not in tests, but before the binary exists. The tradeoff is that the learning curve is genuine: lifetime annotations, ownership transfers, and the distinction between `&T`, `&mut T`, and owned `T` take time to internalize. That investment makes sense for certain workloads; it doesn't make sense for all of them.

I reach for Rust on CPU-bound hot paths, FFI boundaries, and anything I want to ship as a self-contained binary. In practice that means: data processing pipelines where allocator pressure matters, CLI tools I'd otherwise distribute as Go binaries but need finer memory control, and Wasm modules where I want the type system to catch the interface contract. In practice, working with the ownership model means making explicit decisions I'd defer in other languages. When I need shared ownership across async tasks I'm choosing between `Arc<Mutex<T>>` and message passing — and the choice matters for correctness, not just style. Lifetimes in structs that hold references require explicit annotations; I try to design around owned data when possible and reach for `Box<dyn Trait>` when I need dynamic dispatch without coupling to a concrete type. For HTTP services I use Axum with tokio. The router is typed, extractors are typed, and error responses are typed — I define an app-level error enum that implements `IntoResponse`, use `thiserror` for the leaf error types, and `anyhow` internally where I don't need callers to match on variants. Tower middleware handles cross-cutting concerns like tracing and auth. It's a pattern that composes well and keeps the happy path readable. For FFI work the approach depends on the host: N-API bindings for Node.js via `napi-rs`, `wasm-pack` for browser targets, and `cbindgen` for C-compatible headers when integrating with systems that expect a `.so`. Each has friction at the type boundary — the work is managing that interface cleanly, not the Rust side itself.
Rust Adoption Assessment
I look at three things: workload characteristics (is the bottleneck CPU, allocation, or concurrency?), team background (Go and C++ experience shortens the ramp; pure JS/Python background lengthens it), and deployment target (bare binary, Wasm, or embedded changes which parts of the ecosystem you actually need). That scoping determines whether Rust is the right call or whether a different tool gets you there faster.
Service Implementation
My Axum services follow a consistent structure: a typed router with shared state injected via `Extension` or `State`, request types as structs that implement `FromRequest`, and a top-level error type that implements `IntoResponse` and wraps domain-specific errors via `thiserror`. The async runtime is tokio; structured logging goes through `tracing`. The goal is keeping the handler functions thin and the type boundaries explicit.
FFI & Interoperability
For Node.js integration I use `napi-rs`, which generates the N-API glue and handles the JS↔Rust type mapping. For browser targets, `wasm-pack` with `wasm-bindgen` covers most cases. For systems expecting a C ABI, `cbindgen` generates the header from annotated Rust. The actual effort in each case is at the interface boundary — deciding what types cross the FFI and how errors propagate back to the host runtime.
High-Performance APIs
Services where the hot path is CPU-bound — hashing, encoding, parsing, image manipulation — and where latency predictability matters as much as throughput. Rust's lack of a GC pause and explicit allocator control make the tail latency more predictable than runtimes that collect.
CLI Tools & Utilities
Developer tools compiled to a single static binary with no runtime dependency. Cross-compilation to macOS, Linux, and Windows is handled by the toolchain; the result is a binary users can drop in their PATH without installing anything.
WebAssembly Modules
Computation that needs to run in the browser — parsing, cryptographic operations, data transformations — where the logic is already written in Rust or where the binary size and performance characteristics of Wasm from Rust are a better fit than the equivalent JavaScript.
Let's talk Rust.
No pitch. Just a technical conversation about the problem you're working on.