Files
regorus/docs/value/set.md
Anand Krishnamoorthi 41e1303213 feat(value): introduce Set storage abstraction (#740)
Add an opaque `Set` newtype paralleling `Object`, living under
`src/value/set/` with the same module structure (`mod.rs` /
`iter.rs` / `serde.rs`). `Set` wraps `BTreeSet<Value>` today but
exposes only a curated surface: `contains`, `insert`, `remove`,
`iter`, `iter_sorted`, `cursor` (resumable), `is_subset`,
`intersection`, `difference`, serde, and a hand-written `Ord`.
The cursor types are re-exported behind the `rvm` feature so the
follow-up `IterationState::Set` swap can land additively.

To free the `Set` name for the new public type, the crate-internal
`BTreeSet as Set` / `HashSet as Set` aliases in `lib.rs` are
renamed to `MapSet`. All in-tree consumers of the old alias are
updated in lockstep.

`Value::Set` is unchanged in this commit (still wraps
`Rc<BTreeSet<Value>>`); the payload swap and call-site migration
ship in the next PR.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-06-26 13:56:15 -05:00

3.7 KiB

Set

Opaque container for Value::Set's element storage, enabling alternative backends without call-site changes. Pairs with Object under a shared design philosophy.

Design

Set wraps a BTreeSet<Value> today but exposes only a curated method surface (contains, insert, remove, iter, iter_sorted, cursor, is_subset, intersection, union, difference, serde). The inner set is private — callers cannot pattern-match it or hand out references to the backing store, so the backend can change without churn at the ~400 call sites that name Set.

Two iteration methods reflect a real distinction: iter() makes no ordering promise (lets future hash/lazy backends skip sorting work); iter_sorted() guarantees deterministic order (used by serialization and Ord). Cursor types support incremental traversal needed by the RVM iteration state without exposing iterator internals.

Ord is hand-written against iter_sorted rather than derived, so two backends that store elements differently still compare equal when their sorted contents match.

Scenarios enabled

  • Hash-backed storageFxHashSet-backed inner turns O(log n) membership checks into O(1); swap in for policies where elements aren't compared ordinally.
  • Lazy/streaming — wrap a LazySetProvider (DB query, CBOR slice, REST endpoint) and materialize elements on demand.
  • Arena allocation — bumpalo-backed inner for eval-time temporaries; drop the whole arena at query end with zero per-element free cost.
  • FFI-backed — host-language collections (Python set, JS Set) without copying into Rust.
  • Bloom-filter pre-check — front a large backing set with a Bloom filter for fast negative-membership tests on read-mostly allowlists.

Known use cases

  • Azure Policy allowed-values lists — large allowlists (allowed regions, allowed SKUs, allowed image publishers) compared against single resource values. Hash-backed Set turns O(log n) membership checks into O(1).
  • SARIF rule deduplication — collapsing duplicate rule references across thousands of result records. Set-of-objects with structural hashing avoids the BTreeSet sort cost on every insert.
  • RBAC role membership — checking whether a principal belongs to any of dozens of role groups. Hash-backed Set scales to thousands of members with constant-time membership.
  • Azure Policy denied-resource-type sets — exclusion lists used by deny-effect policies; same hash-backed pattern as allowed-values.

Precedents

  • indexmap::IndexSet — opaque newtype that pairs hash lookup with insertion-order iteration; precedent for "Set with alternative ordering semantics behind a stable surface."
  • hashbrown::HashSet — backs Rust's std::collections::HashSet and demonstrates a fully swappable backend behind a stable API.
  • roaring::RoaringBitmap — bitmap-backed integer set. Not applicable to Value keys directly, but a precedent for the broader idea of "Set with alternative storage representations chosen by workload shape."
  • serde_json — note that serde_json has no Set equivalent: its Value enum collapses sets into arrays. Regorus's first-class Set with storage abstraction is therefore unusually well-positioned among JSON value libraries.

Notes

Cursor types are pub (referenced by public IterationState) but not re-exported at the crate root. The crate-internal Set/Map/MapEntry aliases for BTreeSet/BTreeMap in lib.rs were renamed to MapSet/Map/MapEntry when this type landed, to free the Set name for the new public type. Future Array and String abstractions follow the same shape — see docs/value/array.md and docs/value/string.md when they land.