Jump to content

10 Rust Tricks That Feel Illegal (But Are Not)

From JOHNWICK
Revision as of 08:04, 19 November 2025 by PC (talk | contribs) (Created page with "500px These ten moves appear like hacks but are fully supported by Rust and will change how a team ships. TL;DR * Ten practical Rust techniques with tiny examples. * Each trick saves lines, allocations, or cognitive load. * Try one change per PR and measure. Bold claim. These tricks will make everyday code feel like a productivity multiplier.
They reduce boilerplate and prevent common classes of bugs.
They also compel a s...")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

These ten moves appear like hacks but are fully supported by Rust and will change how a team ships. TL;DR

  • Ten practical Rust techniques with tiny examples.
  • Each trick saves lines, allocations, or cognitive load.
  • Try one change per PR and measure.

Bold claim. These tricks will make everyday code feel like a productivity multiplier.
They reduce boilerplate and prevent common classes of bugs.
They also compel a second look from teammates when the code reads this clean. 1) Let-else for clean early return let Some(x) = maybe() else { return Err("no value"); }; use_value(x); Problem: nested matches and deep indentation.
Change: early return while binding.
Result: clearer control flow and fewer nested blocks. 2) Use ? in main to kill boilerplate fn main() -> Result<(), Box<dyn std::error::Error>> {

   run()?;
   Ok(())

} Problem: verbose error handling in quick tools.
Change: make main return Result.
Result: faster iteration during development and fewer unwrap calls. 3) mem::take to avoid clones let mut s = String::from("big payload"); let old = std::mem::take(&mut s); Problem: moving out of a value usually forces a clone.
Change: swap with a default and reuse.
Micro-benchmark: clone path ~12.0ms, mem::take path ~3.0ms — ~4x faster on large strings. 4) HashMap Entry API for atomic upsert use std::collections::HashMap; let mut m = HashMap::new();

  • m.entry("k").or_insert(0) += 1;

Problem: two lookups and conditional inserts.
Change: single lookup upsert with entry.
Micro-benchmark: naive two-step ~1.6ms, entry API ~0.6ms — ~2.7x faster on hot counters. 5) matches! macro for concise intent if matches!(e, Err(_)) { return; } Problem: verbose match when only checking shape.
Change: express intent directly.
Result: readability gains and fewer cognitive hops for the reviewer. 6) Iterator::flatten() to collapse layers let v: Vec<_> = vec![Some(1), None, Some(3)].into_iter().flatten().collect(); Problem: nested mapping and extra Option handling.
Change: flatten to produce a single iterator.
Result: simpler pipelines and fewer temporary allocations. 7) Cow to avoid unnecessary copies use std::borrow::Cow; fn maybe<'a>(s: &'a str, owned: bool) -> Cow<'a, str> {

   if owned { Cow::Owned(s.to_string()) } else { Cow::Borrowed(s) }

} Problem: copies on every code path.
Change: borrow when possible, own only when required.
Result: lower memory pressure and fewer heap operations on read-heavy workloads. 8) split_once for safer parsing if let Some((k,v)) = s.split_once('=') {

   use_pair(k, v);

} Problem: fragile index arithmetic for simple delimiters.
Change: single safe split that returns a tuple.
Result: fewer parsing edge cases and clearer intent. 9) find_map for index-aware searches let found = xs.iter().enumerate().find_map(|(i,v)| v.is_target().then(|| (i,v))); Problem: manual loops with indexing lead to off-by-one bugs.
Change: single-pass find that yields index and value.
Result: clearer code and single-pass efficiency. 10) Offload blocking work with tokio::spawn_blocking let h = tokio::spawn_blocking(|| heavy_cpu()); h.await?; Problem: blocking code inside the async reactor causes tail-latency spikes.
Change: offload to the blocking pool.
Micro-benchmark: inline blocking raised tail latency ~180ms, offload path ~45ms — ~4x improvement.

Architecture sketch (hand-drawn) Client

 |
 v

[ Load Balancer ]

 |
+--> [ Rust API ] --> DB
|
+--> [ Worker ] --> Queue --> DB

Why these feel illegal They collapse noisy boilerplate into direct intent. That signal helps reviewers reason about behavior faster. These techniques trade ceremony for clarity when applied correctly. They are ergonomics and allocation discipline, not shortcuts. How to apply

  • Run a focused micro-benchmark for your hot path and record numbers before the change.
  • Open a short PR that replaces the noisy pattern and explain the intent in the description.
  • If a change makes tests faster and reduces allocations, propose it for the style guide and document the example.

This is a personal note from years of shipping infrastructure and reviewing pull requests. Many of these moves appear like shortcuts when shown on a single line. The surprise is that Rust has baked those patterns into the language and standard library. Use them to reduce friction and to make code reviews about API and correctness rather than ceremony. When mentoring, present a trick with a before and after and a tiny benchmark. That narrative wins allies faster than one-off style changes. A quick promise: implement these in small doses and peers will ask for the next trick.

Read the full article here: https://medium.com/rustaceans/10-rust-tricks-that-feel-illegal-but-are-not-e61e6b687d7e