Skip to content

types: add Struct iteration, and Extend for Struct, ListValue and FieldMask - #712

Merged
iainmcgin merged 3 commits into
anthropics:mainfrom
farmer-data:feat/collection-impls-704
Oct 14, 2026
Merged

iainmcgin merged 3 commits into
anthropics:mainfrom
farmer-data:feat/collection-impls-704

Conversation

@farmer-data

Copy link
Copy Markdown
Contributor

Fixes #704.

Adds the collection items the issue lists, as one change:

  • Struct: len, is_empty, contains_key, iter, IntoIterator for &Struct, &mut Struct and Struct, and Extend<(K, V)> with the Into<String> / Into<Value> bounds of from_fields. A key that is already present has its value replaced, as insert does. The docs state that iteration order is unspecified.
  • ListValue: Extend<V: Into<Value>>, matching from_values.
  • FieldMask: Extend<T: Into<String>>, appending without normalizing, as from_paths does.

Two additions beyond the plan I posted on the issue:

  • Struct::iter_mut. IntoIterator for &mut Struct needs a mutable iterator anyway, and ListValue pairs the two the same way.
  • Three public iterator types, StructIter, StructIterMut and StructIntoIter, re-exported at the crate root. Struct::fields is buffa::__private::HashMap, whose iterators are std's or hashbrown's depending on the std feature, so the IntoIterator impls need a type of their own to name. serde_json::Map takes the same approach. They forward fold and count to the map's iterators, implement ExactSizeIterator and FusedIterator (checked against the inner types in a test), and are #[must_use].

FromIterator keeps its existing item types ((String, Value) and Value). Widening it to match Extend could break inference in existing collect calls, so the crate docs describe the difference instead.

Tests don't depend on hash order. One generic test checks that C: Extend<T> + IntoIterator works for all three types, which is the issue's motivation.

Verification

  • task lint, task doc, task check-nostd and task test-local (4211 passed, 0 failed), with the repository-pinned protoc 33.5. cargo +1.75 check -p buffa-types --all-features with an MSRV-resolved lockfile, as the msrv-check job does.
  • Reviewed with the repo's rust-code-reviewer and rust-api-ergonomics-reviewer per CLAUDE.md: no Critical or High findings. Addressed the Mediums: the iterators forward fold/count, and the docs no longer overstate the FromIterator/Extend symmetry. Also addressed the Lows: a static check of the fused/exact-size claims, #[must_use], Extend doc wording, and HashMap::extend delegation.

…ldMask

Fixes anthropics#704.

The collection-shaped WKTs each forwarded part of their inner
collection's API, so generic code written against `IntoIterator` or
`Extend` worked for some of them and not others, and `Struct` had no way
to count or iterate its entries except through the public field.

- `Struct`: `len`, `is_empty`, `contains_key`, `iter`, `iter_mut`,
  `IntoIterator` for `&Struct`, `&mut Struct` and `Struct`, and
  `Extend<(K, V)>` with the bounds of `from_fields`. A key that is
  already present has its value replaced, as `insert` does.
- `ListValue`: `Extend<V: Into<Value>>`, as `from_values` takes.
- `FieldMask`: `Extend<T: Into<String>>`, appending without normalizing,
  as `from_paths` does.

`FromIterator` keeps its existing item types; widening it could break
inference in existing `collect` calls.

`Struct::fields` is `buffa::__private::HashMap`, whose iterators are
`std`'s or `hashbrown`'s depending on the `std` feature, so the `Struct`
iterators are the newtypes `StructIter`, `StructIterMut` and
`StructIntoIter`, re-exported at the crate root. They forward `fold` and
`count` to the map's iterators, implement `ExactSizeIterator` and
`FusedIterator` (checked against the inner types in a test), and are
`#[must_use]`. The docs state that `Struct`'s iteration order is
unspecified.
@github-actions

Copy link
Copy Markdown

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

farmer-data and others added 2 commits October 13, 2026 21:39
a.extend(b) moves the fields of b and does not carry its unknown
fields; the borrowed spelling is given beside it. The fold comment no
longer lists collect, which drives next.

@iainmcgin iainmcgin left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[claude code] Pushed one commit (3062fd2): two doc edits, with the code and tests unchanged.

  • Extend for Struct: a.extend(b) moves the fields of b and does not carry over its unknown fields. The borrowed spelling a.extend(b.iter().map(|(k, v)| (k, v.clone()))) is given beside it, because a.extend(&b) does not compile where mask.extend(&other) does.
  • The comment on the forwarded fold no longer lists collect, which drives next.

A follow-up for 0.10.0, outside this PR: FromIterator for Struct and ListValue will take the same item types as the Extend impls added here.

@iainmcgin
iainmcgin added this pull request to the merge queue Oct 14, 2026
Merged via the queue into anthropics:main with commit eb71da7 Oct 14, 2026
11 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators Oct 14, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

types: Struct has no len, iter or IntoIterator, and ListValue and FieldMask have no Extend

2 participants