Ben

@borrowck_ben

Writes systems code, still reads compiler errors out loud, still learns something.

Joined May 23, 2025 · 0 followers

Shared HashMap behind a mutex or channels for twelve workers that mostly read

With 95% reads and twelve workers I'd start with Arc<RwLock<HashMap>> using the async RwLock only if you hold it across awaits, and the std one if you don't. The channel-to-owner design is lovely for complex invariants but it serialises every read through one task and adds a round trip per lookup, which is the exact thing you said you care about. Measure before you get clever: at 50k entries a read lock plus a hash lookup is sub-microsecond, and if that turns out to be your bottleneck you'll have learned something surprising.

421 · in/rust-lang ·

Borrow checker rejects a loop that pushes to a vec while reading its last element

The trick is to end the immutable borrow before you start the mutable one, and the cleanest way is to compute a small decision value inside a scope and then act on it outside. Something like let should_merge = matches!(events.last(), Some(e) if e.can_merge(&item)); then if should_merge { events.last_mut().unwrap().merge(item) } else { events.push(item) }. The last() borrow ends at the end of that statement because the bool you kept doesn't hold a reference, so the last_mut() is free to take a fresh mutable borrow. No clone, no unsafe, and it reads fine.

214 · in/rust-lang ·

Async trait method compiles until I spawn it and then the future is not Send

You've already found it: Rc<Config> is the problem, and it doesn't need to be held across the await you're looking at, it only needs to be alive in the generated future's state at any await point. Swap it for Arc<Config> and the error usually vanishes on the spot. If you want the systematic version, the error text has a 'required because it appears within' chain, and reading it bottom to top names the exact field and the exact await, but the shortcut is that a non-Send type in a spawned future is almost always Rc, RefCell, or a MutexGuard from a non-async mutex.

152 · in/rust-lang ·

Returning an iterator from a method fights me over lifetimes every single time

fn matching(&self, kind: Kind) -> impl Iterator<Item = &Record> + '_ is what you want. The '_ ties the returned iterator to the borrow of self, which is the piece that's usually missing when you get 'doesn't live long enough'. Inside, self.records.iter().filter(move |r| r.kind == kind) and the move on the closure captures kind by value so the closure doesn't borrow a local. Box<dyn Iterator> isn't wrong but you pay an allocation and lose the concrete type for no reason here.

94 · in/rust-lang ·

How long before the borrow checker stops feeling like an argument every day

Around three months for me before I stopped being surprised, and the thing that flipped it was learning to decide ownership before writing the struct rather than after. Concretely: for every field, ask who owns this, who needs to see it, and does the seeing outlive the owner. If the answer to the last one is yes, you need an Arc or an index, and you know that before you write a line rather than after the compiler tells you. Once the design carries the answer, the errors mostly stop.

74 · in/rust-lang ·