Repository navigation
types: add Struct iteration, and Extend for Struct, ListValue and FieldMask - #712
Merged
iainmcgin merged 3 commits intoOct 14, 2026
Merged
Conversation
…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.
|
All contributors have signed the CLA ✍️ ✅ |
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
reviewed
Oct 14, 2026
iainmcgin
left a comment
Collaborator
There was a problem hiding this comment.
[claude code] Pushed one commit (3062fd2): two doc edits, with the code and tests unchanged.
Extend for Struct:a.extend(b)moves the fields ofband does not carry over its unknown fields. The borrowed spellinga.extend(b.iter().map(|(k, v)| (k, v.clone())))is given beside it, becausea.extend(&b)does not compile wheremask.extend(&other)does.- The comment on the forwarded
foldno longer listscollect, which drivesnext.
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
approved these changes
Oct 14, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #704.
Adds the collection items the issue lists, as one change:
Struct:len,is_empty,contains_key,iter,IntoIteratorfor&Struct,&mut StructandStruct, andExtend<(K, V)>with theInto<String>/Into<Value>bounds offrom_fields. A key that is already present has its value replaced, asinsertdoes. The docs state that iteration order is unspecified.ListValue:Extend<V: Into<Value>>, matchingfrom_values.FieldMask:Extend<T: Into<String>>, appending without normalizing, asfrom_pathsdoes.Two additions beyond the plan I posted on the issue:
Struct::iter_mut.IntoIterator for &mut Structneeds a mutable iterator anyway, andListValuepairs the two the same way.StructIter,StructIterMutandStructIntoIter, re-exported at the crate root.Struct::fieldsisbuffa::__private::HashMap, whose iterators arestd's orhashbrown's depending on thestdfeature, so theIntoIteratorimpls need a type of their own to name.serde_json::Maptakes the same approach. They forwardfoldandcountto the map's iterators, implementExactSizeIteratorandFusedIterator(checked against the inner types in a test), and are#[must_use].FromIteratorkeeps its existing item types ((String, Value)andValue). Widening it to matchExtendcould break inference in existingcollectcalls, so the crate docs describe the difference instead.Tests don't depend on hash order. One generic test checks that
C: Extend<T> + IntoIteratorworks for all three types, which is the issue's motivation.Verification
task lint,task doc,task check-nostdandtask test-local(4211 passed, 0 failed), with the repository-pinned protoc 33.5.cargo +1.75 check -p buffa-types --all-featureswith an MSRV-resolved lockfile, as themsrv-checkjob does.rust-code-reviewerandrust-api-ergonomics-reviewerper CLAUDE.md: no Critical or High findings. Addressed the Mediums: the iterators forwardfold/count, and the docs no longer overstate theFromIterator/Extendsymmetry. Also addressed the Lows: a static check of the fused/exact-size claims,#[must_use],Extenddoc wording, andHashMap::extenddelegation.