|
| 1 | +# Set |
| 2 | + |
| 3 | +Opaque container for `Value::Set`'s element storage, enabling alternative |
| 4 | +backends without call-site changes. Pairs with [`Object`](object.md) under |
| 5 | +a shared design philosophy. |
| 6 | + |
| 7 | +## Design |
| 8 | + |
| 9 | +`Set` wraps a `BTreeSet<Value>` today but exposes only a curated method |
| 10 | +surface (`contains`, `insert`, `remove`, `iter`, `iter_sorted`, `cursor`, |
| 11 | +`is_subset`, `intersection`, `difference`, serde). The inner set is |
| 12 | +private — callers cannot pattern-match it or hand out references to the |
| 13 | +backing store, so the backend can change without churn at the ~400 call |
| 14 | +sites that name `Set`. |
| 15 | + |
| 16 | +Two iteration methods reflect a real distinction: `iter()` makes no |
| 17 | +ordering promise (lets future hash/lazy backends skip sorting work); |
| 18 | +`iter_sorted()` guarantees deterministic order (used by serialization and |
| 19 | +`Ord`). Cursor types support incremental traversal needed by the RVM |
| 20 | +iteration state without exposing iterator internals. |
| 21 | + |
| 22 | +`Ord` is hand-written against `iter_sorted` rather than derived, so two |
| 23 | +backends that store elements differently still compare equal when their |
| 24 | +sorted contents match. |
| 25 | + |
| 26 | +## Scenarios enabled |
| 27 | + |
| 28 | +- **Hash-backed storage** — `FxHashSet`-backed inner turns O(log n) |
| 29 | + membership checks into O(1); swap in for policies where elements aren't |
| 30 | + compared ordinally. |
| 31 | +- **Lazy/streaming** — wrap a `LazySetProvider` (DB query, CBOR slice, |
| 32 | + REST endpoint) and materialize elements on demand. |
| 33 | +- **Arena allocation** — bumpalo-backed inner for eval-time temporaries; |
| 34 | + drop the whole arena at query end with zero per-element free cost. |
| 35 | +- **FFI-backed** — host-language collections (Python set, JS Set) without |
| 36 | + copying into Rust. |
| 37 | +- **Bloom-filter pre-check** — front a large backing set with a Bloom |
| 38 | + filter for fast negative-membership tests on read-mostly allowlists. |
| 39 | + |
| 40 | +## Known use cases |
| 41 | + |
| 42 | +- **Azure Policy allowed-values lists** — large allowlists (allowed |
| 43 | + regions, allowed SKUs, allowed image publishers) compared against |
| 44 | + single resource values. Hash-backed Set turns O(log n) membership |
| 45 | + checks into O(1). |
| 46 | +- **SARIF rule deduplication** — collapsing duplicate rule references |
| 47 | + across thousands of result records. Set-of-objects with structural |
| 48 | + hashing avoids the BTreeSet sort cost on every insert. |
| 49 | +- **RBAC role membership** — checking whether a principal belongs to any |
| 50 | + of dozens of role groups. Hash-backed Set scales to thousands of |
| 51 | + members with constant-time membership. |
| 52 | +- **Azure Policy denied-resource-type sets** — exclusion lists used by |
| 53 | + deny-effect policies; same hash-backed pattern as allowed-values. |
| 54 | + |
| 55 | +## Precedents |
| 56 | + |
| 57 | +- **`indexmap::IndexSet`** — opaque newtype that pairs hash lookup with |
| 58 | + insertion-order iteration; precedent for "Set with alternative |
| 59 | + ordering semantics behind a stable surface." |
| 60 | +- **`hashbrown::HashSet`** — backs Rust's `std::collections::HashSet` |
| 61 | + and demonstrates a fully swappable backend behind a stable API. |
| 62 | +- **`roaring::RoaringBitmap`** — bitmap-backed integer set. Not |
| 63 | + applicable to `Value` keys directly, but a precedent for the broader |
| 64 | + idea of "Set with alternative storage representations chosen by |
| 65 | + workload shape." |
| 66 | +- **`serde_json`** — note that `serde_json` has no Set equivalent: its |
| 67 | + Value enum collapses sets into arrays. Regorus's first-class Set with |
| 68 | + storage abstraction is therefore unusually well-positioned among JSON |
| 69 | + value libraries. |
| 70 | + |
| 71 | +## Notes |
| 72 | + |
| 73 | +Cursor types are `pub` (referenced by public `IterationState`) but not |
| 74 | +re-exported at the crate root. The crate-internal `Set`/`Map`/`MapEntry` |
| 75 | +aliases for `BTreeSet`/`BTreeMap` in `lib.rs` were renamed to |
| 76 | +`MapSet`/`Map`/`MapEntry` when this type landed, to free the `Set` name |
| 77 | +for the new public type. Future Array and String abstractions follow the |
| 78 | +same shape — see `docs/value/array.md` and `docs/value/string.md` when |
| 79 | +they land. |
0 commit comments