Skip to content

feat(ts): read a boxed iterator as std's Iterator for Box<I> does - #490

Merged
kixelated merged 6 commits into
mainfrom
quest/m3/ts-box-folds
Nov 3, 2026
Merged

kixelated merged 6 commits into
mainfrom
quest/m3/ts-box-folds

Conversation

@kixelated

@kixelated kixelated commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

rust2-ts erased a Box<I> and folded what it held with that iterator's own folds. std's Iterator for Box<I> forwards only next, size_hint, nth, last, next_back, nth_back, and len/is_empty, and reads everything else by next, advance_by and the folds included. So Box::new(a.map(f).zip(b.map(g)).skip(1)).count() made the skipped pair whole, where native skips it one side at a time through the zip's nth. It also read a zip's excess left item in a different order.

The same gap affected Box<dyn Iterator>, which went through rt.iterDyn and so forwarded advance_by. That is right for &mut dyn Iterator but not for a box, which reads advance_by by next (for example through Chain::nth).

Approach

  • rt.iterBox(it): a runtime wrapper that forwards exactly what std's box forwards. Its last dispatches to the held iterator's own last, in both ownedLast and the non-dropping iterLast. Everything else falls back to the Iter defaults, which read by next. Dyn and Boxed now share a Forward base, and Dyn adds advanceBy/advanceBackBy.
  • into_iter wraps by the box layers of the type it was given (boxes):
    • boxes before the first reference become one rt.iterBox;
    • a box past a reference becomes rt.iterBorrow(rt.iterBox(..)), so &mut Box<I> folds and takes last by next, as std's &mut I over a box does.
  • call now passes the original self_ty and receiver to into_iter, so a consumer sees the box.

Proof: lang/dyns gains dyns::boxes. It runs a skip of a TRA zip whose items log as they are made and dropped, through every consumer: forwarded (size_hint, len, next, next_back, nth, nth_back, last, Box<Box<I>>::last) and by-next (count, fold, for_each, position, find, any, max_by_key, collect, rev().fold, sum, skip, step_by, Chain::nth's advance_by). Each is checked owned, behind &mut, as a Box<&mut I>, and as Box<dyn> vs &mut dyn. A non-dropping logging zip covers last in the iterLast path. Mutation checks pass: forwarding advanceBy in Boxed, dropping the last dispatch, or dropping the borrowed-box wrap each fails the differential test.

Impact

  • Translator output: a boxed iterator, and a reference to one, is now rt.iterBox(..) where it is read. Snapshot churn in lang/dyns and lang/iters.
  • Runtime: new export rt.iterBox. Nothing removed.

Alternatives

  • Reporting the box layers from peel_iter, which is exact per layer. Collapsing them in boxes gives the same semantics with less code, since iterBorrow(iterBox(x)) reads alike whatever x holds.
  • Wrapping only at Box<dyn> coercion. That misses Box<I> of sized iterators, and &mut *boxed_dyn would wrongly lose advance_by forwarding.

Follow-ups

Review

Codex gpt-6.1-sol, medium, via quest review.

  • Round 1 (ff468ae): one finding. The non-dropping iterLast read a box by next instead of forwarding last. Fixed in f1f5ee6, with a fixture case that fails without the fix.
  • Round 2 (602d16e): no findings.

🤖 Generated with Claude Code

kixelated and others added 6 commits November 2, 2026 21:31
Quest-Claim: c993241894bc93d0
A box of an iterator, a trait object or not, owned or behind a `&mut`, is
rt.iterBox of it: it forwards next, size_hint, nth, last, and the
double-ended and exact-size methods, and reads the rest by next,
advance_by and the folds included, where the translation folded what the
box held by its own folds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…does

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kixelated kixelated changed the title A boxed iterator folds as std's box does feat(ts): read a boxed iterator as std's Iterator for Box<I> does Nov 3, 2026
@kixelated
kixelated marked this pull request as ready for review November 3, 2026 06:13
@coderabbitai

coderabbitai Bot commented Nov 3, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 4 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

Run configuration
  • Source mode: Auto
  • Repository: Unavailable
  • Branch: Unavailable
  • File: No configuration file from this repository applied
  • Commit: Unavailable
  • Layers: Organization UI
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: f6c66aad-ba36-4eef-8ff3-e7c24f59dce0
Commits

Reviewing files that changed from the base of the PR and between 1eb763b and 602d16e.

Files selected for processing: 11
  • quest/m3/README.md
  • quest/m3/ts-box-folds.md
  • ts/README.md
  • ts/fixtures/lang/dyns/expected/core/option.ts
  • ts/fixtures/lang/dyns/expected/dyns.ts
  • ts/fixtures/lang/dyns/expected/lib.ts
  • ts/fixtures/lang/dyns/src/dyns.rs
  • ts/fixtures/lang/dyns/src/lib.rs
  • ts/fixtures/lang/iters/expected/iters.ts
  • ts/runtime/rt/iter.ts
  • ts/src/lower/iter.rs
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

CodeRabbit Verify

  • Run CodeRabbit Verify to test the changes in this pull request.

@kixelated
kixelated added this pull request to the merge queue Nov 3, 2026
Merged via the queue into main with commit eae103e Nov 3, 2026
5 checks passed
@kixelated
kixelated deleted the quest/m3/ts-box-folds branch November 3, 2026 06:20
kixelated added a commit that referenced this pull request Nov 3, 2026
Keeps one box reader, main's rt.iterBox from #490, and deletes this branch's rt.iterBoxed with its own wrapping in into_iter. iterBox already forwards only what std's Box<I> does, so a box of the crate's own iterator, or of an adaptor holding one, never reaches its by-value overrides. ends' boxed Overrides cases and zips' boxed reads stay beside #490's dyns cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant