ADR-233: A Lambda That Pauses Is Handed to std’s Pausing Counterpart

Status: Accepted Date: September 27, 2026 Answers: #187, a lambda that pauses is refused where std takes it — under the owner’s Nikaia does not build walls, it takes them down, and the special case pays, not the general one Refines: ADR-172 D3 (the trait for a sequence whose step pauses, which waited for a second producer), ADR-277 D12 (a std entry’s lambda is a Rust signature’s) Related: ADR-277 D13 (Rust’s async closure, measured), ADR-277 D11 (a run parameter is impl AsyncFn), Part II 12.2 (no pause while a lock is held), ADR-293 D29 (NK2105)

1. Context

Nikaia has no async and no await: any function may pause, and the compiler works out which do (ADR-055). A lambda is a function, so this is a correct program:

let sizes = urls.iter().map(fn(u) { fetch(u).len() }).collect()

std takes a lambda in ten places, and each entry describes a Rust signature whose closure is synchronous: Iterator::map takes FnMut, and an async closure handed to it makes an iterator of futures, which is another program. ADR-277 gives a parameter declared in this language its pausing shape; this record gives std’s entries theirs.

2. Decisions

D1 — map and filter of a sequence hand back a sequence whose step pauses.

Where the lambda handed to Seq::map or Seq::filter pauses, the result is a Seq[U] pauses — the type io::lines() has had since ADR-172 D1 — and everything that type already means applies:

Below, it is nikaia_std::seq::Paused, built on a Step trait whose step is an async fn. That trait is the one ADR-172 D3 deferred until a second producer needs one; this is the second producer. It is the crate’s own, not futures_core::Stream, for the reason D3 gave. The lambda is the language below’s async closure (AsyncFnMut, ADR-277 D13), with no box and no move: std runs it, it does not keep it (ADR-277 D11).

Nothing is walked where the map is written, as with any map: the lambda runs when something walks the result, and that walk is what pauses.

D2 — The eager entries await the lambda where they would have called it.

list::ListExt::map (a list’s own map), Vec::sort_by_key, Entry::or_insert_with and Entry::and_modify run the lambda during the call, so the call pauses. Each has a counterpart in nikaia_std::seq that takes the async closure and awaits it where the synchronous entry calls its closure: the same order, the same number of calls. sort_by_key works each key out once rather than at every comparison — a key that pauses cannot be asked for again and again — and sorts stably, as the entry does. The entry helpers serve both kinds of map.

A list’s map resolves to its entry in the checker (ListExt::map is the key, not Vec::map), so its lambda’s parameter is typed and the list it takes is taken: reading it afterwards is NK2105, not rustc’s borrow of moved value.

D3 — A pause inside a door is NK2202, by Part II 12.2’s rule.

The other four of the ten — access and update on Locked and SharedMut, and access_all/update_all — run their lambda while a lock is held. A pause there holds the lock for as long as the wait takes, which Part II 12.2 forbids and nothing asked. The checker refuses every call that may pause inside a door, and a for over a sequence whose step pauses, with NK2202, saying what is held and how to move the call out. It is counted on a tally line of its own, a pause with a lock held.

D4 — Two small walls in the way of the first programs.

D5 — What pays is the lambda that pauses.

A lambda that does not pause is lowered to the entry’s own synchronous closure, as if no counterpart existed. The counterparts are chosen by the checker, per call, from whether the lambda’s body was seen to pause — which the checker already worked out for ADR-277 D7 — and from the receiver’s type, which the emitter has none of (ADR-288).

3. What is still refused

A lambda handed to a method whose receiver the checker could not resolve. There is no signature to say what shape the lambda takes, so a pausing one is refused by the lowering, which says so.

This site is open source. Improve this page.