From 3fd8afead8756db7ce5cee2c35848dbefe817487 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Rze=C5=BAnicki?= Date: Thu, 3 Sep 2026 18:05:29 +0200 Subject: [PATCH 1/4] splitting the API into ZipperValue and ZipperValueAt --- pathmap-derive/src/lib.rs | 30 +++++++++++++++++++++++++ src/dependent_zipper.rs | 9 ++++++++ src/empty_zipper.rs | 3 +++ src/experimental/zipper_algebra.rs | 2 +- src/overlay_zipper.rs | 11 ++++++++- src/prefix_zipper.rs | 6 +++++ src/product_zipper.rs | 13 +++++++++++ src/trie_ref.rs | 9 ++++++++ src/write_zipper.rs | 7 ++++++ src/zipper.rs | 36 ++++++++++++++++++++++++------ 10 files changed, 117 insertions(+), 9 deletions(-) diff --git a/pathmap-derive/src/lib.rs b/pathmap-derive/src/lib.rs index 24007101..c0abafb3 100644 --- a/pathmap-derive/src/lib.rs +++ b/pathmap-derive/src/lib.rs @@ -8,6 +8,7 @@ use std::collections::BTreeSet; enum PolyZipperTrait { Zipper, ZipperValues, + ZipperValuesAt, ZipperReadOnlyValues, ZipperReadOnlyConditionalValues, ZipperReadOnlyConditionalIteration, @@ -26,6 +27,7 @@ impl PolyZipperTrait { match ident.to_string().as_str() { "Zipper" => Some(Self::Zipper), "ZipperValues" => Some(Self::ZipperValues), + "ZipperValuesAt" => Some(Self::ZipperValuesAt), "ZipperReadOnlyValues" => Some(Self::ZipperReadOnlyValues), "ZipperReadOnlyConditionalValues" => Some(Self::ZipperReadOnlyConditionalValues), "ZipperReadOnlyConditionalIteration" => Some(Self::ZipperReadOnlyConditionalIteration), @@ -47,6 +49,7 @@ fn all_poly_zipper_traits() -> BTreeSet { BTreeSet::from([ Zipper, ZipperValues, + ZipperValuesAt, ZipperReadOnlyValues, ZipperReadOnlyConditionalValues, ZipperReadOnlyConditionalIteration, @@ -118,6 +121,11 @@ fn add_trait_dependencies(traits: &mut BTreeSet) { } } if traits.contains(&ZipperInfallibleSubtries) { + if traits.insert(ZipperValuesAt) { + changed = true; + } + } + if traits.contains(&ZipperValuesAt) { if traits.insert(ZipperValues) { changed = true; } @@ -312,7 +320,28 @@ fn derive_poly_zipper_with_traits( #(#variant_arms => inner.val(),)* } } + } + }) + } else { + None + }; + // Generate ZipperValuesAt trait implementation + let zipper_values_at_impl = if traits.contains(&PolyZipperTrait::ZipperValuesAt) { + let variant_arms = &variant_arms; + let zipper_values_where = if include_where_clause { + quote! { + where + #(#inner_types: pathmap::zipper::ZipperValuesAt,)* + #where_clause + } + } else { + quote! {} + }; + Some(quote! { + impl #impl_generics pathmap::zipper::ZipperValuesAt for #enum_name #ty_generics + #zipper_values_where + { fn val_at>(&self, path: K) -> Option<&V> { match self { #(#variant_arms => inner.val_at(path),)* @@ -825,6 +854,7 @@ fn derive_poly_zipper_with_traits( #(#from_impls)* #zipper_impl #zipper_values_impl + #zipper_values_at_impl #zipper_read_only_values_impl #zipper_read_only_conditional_values_impl // #zipper_forking_impl diff --git a/src/dependent_zipper.rs b/src/dependent_zipper.rs index 63627d05..e486f891 100644 --- a/src/dependent_zipper.rs +++ b/src/dependent_zipper.rs @@ -237,6 +237,15 @@ impl<'trie, PrimaryZ, SecondaryZ, V, C, F : Clone + for <'a> FnOnce(C, &'a [u8], self.primary.val() } } +} + +impl<'trie, PrimaryZ, SecondaryZ, V, C, F : Clone + for <'a> FnOnce(C, &'a [u8], usize) -> (C, Option)> ZipperValuesAt + for DependentProductZipperG<'trie, PrimaryZ, SecondaryZ, V, C, F> + where + V: Clone + Send + Sync, + PrimaryZ: ZipperMoving + ZipperValuesAt, + SecondaryZ: ZipperMoving + ZipperValuesAt, +{ fn val_at>(&self, path: K) -> Option<&V> { if let Some(idx) = self.factor_idx(true) { self.secondary[idx].val_at(path) diff --git a/src/empty_zipper.rs b/src/empty_zipper.rs index e23d3f35..cb7c7c95 100644 --- a/src/empty_zipper.rs +++ b/src/empty_zipper.rs @@ -90,6 +90,9 @@ impl ZipperIteration for EmptyZipper { impl ZipperValues for EmptyZipper { fn val(&self) -> Option<&V> { None } +} + +impl ZipperValuesAt for EmptyZipper { fn val_at>(&self, _path: K) -> Option<&V> { None } } diff --git a/src/experimental/zipper_algebra.rs b/src/experimental/zipper_algebra.rs index 19634e2c..325df57a 100644 --- a/src/experimental/zipper_algebra.rs +++ b/src/experimental/zipper_algebra.rs @@ -2856,7 +2856,7 @@ mod zipper_algebra_poly { use pathmap_derive::PolyZipperExplicit; #[derive(PolyZipperExplicit)] - #[poly_zipper_explicit(traits(ZipperMoving, ZipperValues, ZipperConcrete))] + #[poly_zipper_explicit(traits(ZipperMoving, ZipperValues, ZipperValuesAt, ZipperConcrete))] pub(super) enum SomeMutRefZ<'a, 'trie, 'path, V: Clone + Send + Sync + Unpin, A: Allocator> { RZ(&'a mut ReadZipperUntracked<'trie, 'path, V, A>), RZT(&'a mut ReadZipperTracked<'trie, 'path, V, A>), diff --git a/src/overlay_zipper.rs b/src/overlay_zipper.rs index 5cae3fd3..df66c920 100644 --- a/src/overlay_zipper.rs +++ b/src/overlay_zipper.rs @@ -18,7 +18,7 @@ use fast_slice_utils::find_prefix_overlap; use crate::utils::{BitMask, ByteMask}; -use crate::zipper::{Zipper, ZipperMoving, ZipperIteration, ZipperValues}; +use crate::zipper::{Zipper, ZipperMoving, ZipperIteration, ZipperValues, ZipperValuesAt}; /// Zipper that traverses a virtual trie formed by fusing the tries of two other zippers pub struct OverlayZipper @@ -104,6 +104,15 @@ impl ZipperValues fn val(&self) -> Option<&OutV> { (self.mapping)(self.a.val(), self.b.val()) } +} + +impl ZipperValuesAt + for OverlayZipper + where + AZipper: ZipperValuesAt, + BZipper: ZipperValuesAt, + Mapping: for<'a> Fn(Option<&'a AV>, Option<&'a BV>) -> Option<&'a OutV>, +{ fn val_at>(&self, path: K) -> Option<&OutV> { (self.mapping)(self.a.val_at(&path), self.b.val_at(&path)) } diff --git a/src/prefix_zipper.rs b/src/prefix_zipper.rs index c2238166..4d94d91f 100644 --- a/src/prefix_zipper.rs +++ b/src/prefix_zipper.rs @@ -242,6 +242,12 @@ impl<'prefix, Z, V> ZipperValues for PrefixZipper<'prefix, Z> } self.source.val() } +} + +impl<'prefix, Z, V> ZipperValuesAt for PrefixZipper<'prefix, Z> + where + Z: ZipperValuesAt +{ fn val_at>(&self, path: K) -> Option<&V> { let path = self.adjust_lookup_path(path.as_ref())?; self.source.val_at(path) diff --git a/src/product_zipper.rs b/src/product_zipper.rs index dc350bd5..80840d24 100644 --- a/src/product_zipper.rs +++ b/src/product_zipper.rs @@ -308,6 +308,9 @@ impl<'trie, V: Clone + Send + Sync + Unpin + 'trie, A: Allocator + 'trie> Zipper fn val(&self) -> Option<&V> { unsafe{ self.z.get_val() } } +} + +impl<'trie, V: Clone + Send + Sync + Unpin + 'trie, A: Allocator + 'trie> ZipperValuesAt for ProductZipper<'_, 'trie, V, A> { fn val_at>(&self, path: K) -> Option<&V> { unsafe{ self.z.get_val_at(path) } } @@ -562,6 +565,15 @@ impl<'trie, PrimaryZ, SecondaryZ, V> ZipperValues self.primary.val() } } +} + +impl<'trie, PrimaryZ, SecondaryZ, V> ZipperValuesAt + for ProductZipperG<'trie, PrimaryZ, SecondaryZ, V> + where + V: Clone + Send + Sync, + PrimaryZ: ZipperMoving + ZipperValuesAt, + SecondaryZ: ZipperMoving + ZipperValuesAt, +{ fn val_at>(&self, path: K) -> Option<&V> { if let Some(idx) = self.factor_idx(true) { self.secondary[idx].val_at(path) @@ -887,6 +899,7 @@ impl ZipperAbsolutePath for OneFactor { zipper_impl_ impl ZipperMoving for OneFactor { zipper_impl_lens!(ZipperMoving self => self.z); } impl ZipperIteration for OneFactor { zipper_impl_lens!(ZipperIteration self => self.z); } impl > ZipperValues for OneFactor { zipper_impl_lens!(ZipperValues self => self.z); } +impl > ZipperValuesAt for OneFactor { zipper_impl_lens!(ZipperValuesAt self => self.z); } impl > ZipperForking for OneFactor { type ReadZipperT<'a> = Z::ReadZipperT<'a> where Z: 'a; zipper_impl_lens!(ZipperForking self => self.z); } impl > ZipperSubtries for OneFactor { zipper_impl_lens!(ZipperSubtries self => self.z); } impl > ZipperInfallibleSubtries for OneFactor { zipper_impl_lens!(ZipperInfallibleSubtries self => self.z); } diff --git a/src/trie_ref.rs b/src/trie_ref.rs index 7aade28e..3157434a 100644 --- a/src/trie_ref.rs +++ b/src/trie_ref.rs @@ -266,6 +266,9 @@ impl ZipperValues for TrieRefBo fn val(&self) -> Option<&V> { self.get_val() } +} + +impl ZipperValuesAt for TrieRefBorrowed<'_, V, A> { fn val_at>(&self, path: K) -> Option<&V> { self.get_val_at(path) } @@ -646,6 +649,9 @@ impl ZipperValues for TrieRefOw None } } +} + +impl ZipperValuesAt for TrieRefOwned { fn val_at>(&self, path: K) -> Option<&V> { if self.is_valid() { TrieRefBorrowed::new_with_key_and_path_in( @@ -844,6 +850,9 @@ impl ZipperValues for TrieRef<' TrieRef::Owned(trie_ref) => trie_ref.val(), } } +} + +impl ZipperValuesAt for TrieRef<'_, V, A> { fn val_at>(&self, path: K) -> Option<&V> { match self { TrieRef::Borrowed(trie_ref) => trie_ref.val_at(path), diff --git a/src/write_zipper.rs b/src/write_zipper.rs index 5dfbd5df..a9786021 100644 --- a/src/write_zipper.rs +++ b/src/write_zipper.rs @@ -390,6 +390,9 @@ impl<'a, V: Clone + Send + Sync + Unpin, A: Allocator + 'a> Zipper for WriteZipp impl<'a, V: Clone + Send + Sync + Unpin, A: Allocator + 'a> ZipperValues for WriteZipperTracked<'a, '_, V, A>{ fn val(&self) -> Option<&V> { self.z.val() } +} + +impl<'a, V: Clone + Send + Sync + Unpin, A: Allocator + 'a> ZipperValuesAt for WriteZipperTracked<'a, '_, V, A>{ fn val_at>(&self, path: K) -> Option<&V> { self.z.val_at(path) } } @@ -550,6 +553,9 @@ impl<'a, V: Clone + Send + Sync + Unpin, A: Allocator + 'a> Zipper for WriteZipp impl<'a, V: Clone + Send + Sync + Unpin, A: Allocator + 'a> ZipperValues for WriteZipperUntracked<'a, '_, V, A> { fn val(&self) -> Option<&V> { self.z.val() } +} + +impl<'a, V: Clone + Send + Sync + Unpin, A: Allocator + 'a> ZipperValuesAt for WriteZipperUntracked<'a, '_, V, A> { fn val_at>(&self, path: K) -> Option<&V> { self.z.val_at(path) } } @@ -717,6 +723,7 @@ impl Clone for WriteZipp impl Zipper for WriteZipperOwned { zipper_impl_lens!(Zipper self => self.z); } impl ZipperValues for WriteZipperOwned { zipper_impl_lens!(ZipperValues self => self.z); } +impl ZipperValuesAt for WriteZipperOwned { zipper_impl_lens!(ZipperValuesAt self => self.z); } impl ZipperInfallibleSubtries for WriteZipperOwned { zipper_impl_lens!(ZipperInfallibleSubtries self => self.z); } impl ZipperMoving for WriteZipperOwned { zipper_impl_lens!(ZipperMoving self => self.z); } impl ZipperPathBuffer for WriteZipperOwned { zipper_impl_lens!(ZipperPathBuffer self => self.z); } diff --git a/src/zipper.rs b/src/zipper.rs index 95314fe9..c0049175 100644 --- a/src/zipper.rs +++ b/src/zipper.rs @@ -59,12 +59,6 @@ pub trait ZipperValues { /// will provide a longer-lived reference to the value. fn val(&self) -> Option<&V>; - /// Returns a refernce to the value at `path`, relative to the zipper's focus, or `None` if there is no value - /// - /// If you have a zipper type that implements [ZipperReadOnlyValues] then [ZipperReadOnlyValues::get_val_at] - /// will provide a longer-lived reference to the value. - fn val_at>(&self, path: K) -> Option<&V>; - /// Deprecated alias for [ZipperValues::val] #[deprecated] //GOAT-old-names fn value(&self) -> Option<&V> { @@ -72,6 +66,22 @@ pub trait ZipperValues { } } +/// Provides random access to values below the zipper's current focus. +/// +/// Unlike [ZipperValues::val], this capability requires the zipper to be able +/// to return a stable reference to a value at an arbitrary relative path. +/// Computed or virtual zippers may therefore implement [ZipperValues] without +/// implementing this trait. +pub trait ZipperValuesAt: ZipperValues { + /// Returns a reference to the value at `path`, relative to the zipper's + /// focus, or `None` if there is no value. + /// + /// If you have a zipper type that implements [ZipperReadOnlyValues] then + /// [ZipperReadOnlyValues::get_val_at] will provide a longer-lived reference + /// to the value. + fn val_at>(&self, path: K) -> Option<&V>; +} + /// Method to fork a read zipper from the parent zipper pub trait ZipperForking { /// The read-zipper type returned from [fork_read_zipper](ZipperForking::fork_read_zipper) @@ -548,7 +558,7 @@ impl<'a, V: Clone + Send + Sync, A: Allocator> OpaqueAbstractNodeRef<'a, V, A> { pub struct OpaqueTrieNodeRef<'trie, V: Clone + Send + Sync, A: Allocator>(pub(crate) &'trie TrieNodeODRc); /// Similar to [ZipperSubtries], but with the stronger guarantee that subtrie access will be constant-time and won't fail -pub trait ZipperInfallibleSubtries: ZipperValues + Zipper { +pub trait ZipperInfallibleSubtries: ZipperValuesAt + Zipper { /// Returns a new [PathMap] containing everything below the zipper's focus fn make_map(&self) -> PathMap; @@ -821,6 +831,8 @@ macro_rules! zipper_impl_lens { }; (ZipperValues $s: ident => $e:expr) => { fn val(&$s) -> Option<&V> { $e.val() } + }; + (ZipperValuesAt $s: ident => $e:expr) => { fn val_at>(&$s, path: K) -> Option<&V> { $e.val_at(path) } }; (ZipperForking $s: ident => $e:expr) => { @@ -901,6 +913,7 @@ impl ZipperAbsolutePath for Box { zipper_impl_lens!( impl ZipperMoving for Box { zipper_impl_lens!(ZipperMoving self => (**self)); } impl ZipperIteration for Box { zipper_impl_lens!(ZipperIteration self => (**self)); } impl > ZipperValues for Box { zipper_impl_lens!(ZipperValues self => (**self)); } +impl > ZipperValuesAt for Box { zipper_impl_lens!(ZipperValuesAt self => (**self)); } impl > ZipperForking for Box { type ReadZipperT<'a> = Z::ReadZipperT<'a> where Self: 'a; zipper_impl_lens!(ZipperForking self => (**self)); } impl > ZipperSubtries for Box { zipper_impl_lens!(ZipperSubtries self => (**self)); } impl > ZipperInfallibleSubtries for Box { zipper_impl_lens!(ZipperInfallibleSubtries self => (**self)); } @@ -918,6 +931,7 @@ impl ZipperAbsolutePath for &mut Z { zipper_impl_lens!( impl ZipperMoving for &mut Z { zipper_impl_lens!(ZipperMoving self => (**self)); } impl ZipperIteration for &mut Z { zipper_impl_lens!(ZipperIteration self => (**self)); } impl > ZipperValues for &mut Z { zipper_impl_lens!(ZipperValues self => (**self)); } +impl > ZipperValuesAt for &mut Z { zipper_impl_lens!(ZipperValuesAt self => (**self)); } impl > ZipperForking for &mut Z { type ReadZipperT<'a> = Z::ReadZipperT<'a> where Self: 'a; zipper_impl_lens!(ZipperForking self => (**self)); } impl > ZipperSubtries for &mut Z { zipper_impl_lens!(ZipperSubtries self => (**self)); } impl > ZipperInfallibleSubtries for &mut Z { zipper_impl_lens!(ZipperInfallibleSubtries self => (**self)); } @@ -970,6 +984,7 @@ impl Drop for ReadZipperTracked<'_, '_, V, impl Zipper for ReadZipperTracked<'_, '_, V, A> { zipper_impl_lens!(Zipper self => self.z); } impl ZipperValues for ReadZipperTracked<'_, '_, V, A>{ zipper_impl_lens!(ZipperValues self => self.z); } +impl ZipperValuesAt for ReadZipperTracked<'_, '_, V, A>{ zipper_impl_lens!(ZipperValuesAt self => self.z); } impl ZipperSubtries for ReadZipperTracked<'_, '_, V, A> { zipper_impl_lens!(ZipperSubtries self => self.z); } impl ZipperInfallibleSubtries for ReadZipperTracked<'_, '_, V, A> { zipper_impl_lens!(ZipperInfallibleSubtries self => self.z); } impl<'trie, V: Clone + Send + Sync + Unpin + 'trie, A: Allocator + 'trie> ZipperMoving for ReadZipperTracked<'trie, '_, V, A> { zipper_impl_lens!(ZipperMoving self => self.z); } @@ -1058,6 +1073,7 @@ pub struct ReadZipperUntracked<'a, 'path, V: Clone + Send + Sync, A: Allocator = impl Zipper for ReadZipperUntracked<'_, '_, V, A> { zipper_impl_lens!(Zipper self => self.z); } impl ZipperValues for ReadZipperUntracked<'_, '_, V, A> { zipper_impl_lens!(ZipperValues self => self.z); } +impl ZipperValuesAt for ReadZipperUntracked<'_, '_, V, A> { zipper_impl_lens!(ZipperValuesAt self => self.z); } impl<'trie, V: Clone + Send + Sync + Unpin + 'trie, A: Allocator + 'trie> ZipperPathBuffer for ReadZipperUntracked<'trie, '_, V, A> { zipper_impl_lens!(ZipperPathBuffer self => self.z); } impl<'trie, V: Clone + Send + Sync + Unpin + 'trie, A: Allocator + 'trie> ZipperIteration for ReadZipperUntracked<'trie, '_, V, A> { zipper_impl_lens!(ZipperIteration self => self.z); } impl<'trie, V: Clone + Send + Sync + Unpin + 'trie, A: Allocator + 'trie> ZipperReadOnlyConditionalIteration<'trie, V> for ReadZipperUntracked<'trie, '_, V, A> { } @@ -1232,6 +1248,9 @@ impl<'trie, V: Clone + Send + Sync + Unpin + 'trie, A: Allocator + 'trie> Zipper impl ZipperValues for ReadZipperOwned { fn val(&self) -> Option<&V> { unsafe{ self.z.get_val() } } +} + +impl ZipperValuesAt for ReadZipperOwned { fn val_at>(&self, path: K) -> Option<&V> { unsafe{ self.z.get_val_at(path) } } } @@ -1470,6 +1489,9 @@ pub(crate) mod read_zipper_core { impl ZipperValues for ReadZipperCore<'_, '_, V, A> { fn val(&self) -> Option<&V> { unsafe{ self.get_val() } } + } + + impl ZipperValuesAt for ReadZipperCore<'_, '_, V, A> { fn val_at>(&self, path: K) -> Option<&V> { unsafe{ self.get_val_at(path) } } } From 982d1bc56793bcb64556febd13216b37eb574759 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Rze=C5=BAnicki?= Date: Thu, 3 Sep 2026 18:44:21 +0200 Subject: [PATCH 2/4] splitting the API into ZipperValue and ZipperValueAt cont'd --- src/arena_compact.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/arena_compact.rs b/src/arena_compact.rs index 086ea772..a201ce22 100644 --- a/src/arena_compact.rs +++ b/src/arena_compact.rs @@ -82,6 +82,7 @@ use std::marker::PhantomData; use fast_slice_utils::starts_with; use crate::alloc::{GlobalAlloc, global_alloc}; +use crate::zipper::ZipperValuesAt; use crate::{ PathMap, morphisms::Catamorphism, @@ -2785,6 +2786,11 @@ where Storage: AsRef<[u8]> fn val(&self) -> Option<&()> { self.get_value().map(|_x| &()) } +} + +impl<'tree, Storage> ZipperValuesAt<()> for ACTZipper<'tree, Storage, ()> +where Storage: AsRef<[u8]> +{ fn val_at>(&self, path: K) -> Option<&()> { self.get_value_at(path.as_ref()).map(|_x| &()) } @@ -2797,6 +2803,11 @@ where Storage: AsRef<[u8]> //GOAT, see soundness discussion in ZipperReadOnlyValues impl below self.get_val() } +} + +impl<'tree, Storage> ZipperValuesAt for ACTZipper<'tree, Storage, u64> +where Storage: AsRef<[u8]> +{ fn val_at>(&self, path: K) -> Option<&u64> { //GOAT, see soundness discussion in ZipperReadOnlyValues impl below self.get_val_at(path) From 1da5011be4c879210e76910623e606757a03fa78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Rze=C5=BAnicki?= Date: Thu, 3 Sep 2026 18:45:27 +0200 Subject: [PATCH 3/4] fix tests after splitting `ZipperValue` --- src/poly_zipper.rs | 2 +- src/prefix_zipper.rs | 1 + src/trie_ref.rs | 2 +- src/zipper.rs | 4 ++-- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/poly_zipper.rs b/src/poly_zipper.rs index 7eb19fab..ce90d250 100644 --- a/src/poly_zipper.rs +++ b/src/poly_zipper.rs @@ -142,7 +142,7 @@ mod tests { // ====================================================================================== // Cocktail of recursive zipper madness #[derive(PolyZipperExplicit)] - #[poly_zipper_explicit(traits(Zipper, ZipperValues, ZipperMoving, ZipperIteration))] + #[poly_zipper_explicit(traits(Zipper, ZipperValues, ZipperValuesAt, ZipperMoving, ZipperIteration))] pub enum ExprFactor<'trie, V: Clone + Send + Sync + Unpin + 'static = ()> { Specific(ReadZipperOwned), Generic(PrefixZipper<'trie, diff --git a/src/prefix_zipper.rs b/src/prefix_zipper.rs index 4d94d91f..9f6ce40d 100644 --- a/src/prefix_zipper.rs +++ b/src/prefix_zipper.rs @@ -693,6 +693,7 @@ mod tests { use crate::zipper::ZipperAbsolutePath; use crate::zipper::ZipperReadOnlyValues; use crate::zipper::ZipperValues; + use crate::zipper::ZipperValuesAt; const PATHS1: &[(&[u8], u64)] = &[ (b"0000", 0), (b"00000", 1), diff --git a/src/trie_ref.rs b/src/trie_ref.rs index 3157434a..547e5909 100644 --- a/src/trie_ref.rs +++ b/src/trie_ref.rs @@ -1147,7 +1147,7 @@ mod tests { #[test] fn trie_ref_val_at_test() { - fn assert_val_at>(trie_ref: T) { + fn assert_val_at>(trie_ref: T) { assert_eq!(trie_ref.val(), None); assert_eq!(trie_ref.val_at(b"root:a:new_a"), Some(&10)); assert_eq!(trie_ref.val_at(b"root:a:nested:deep"), Some(&11)); diff --git a/src/zipper.rs b/src/zipper.rs index c0049175..38339eeb 100644 --- a/src/zipper.rs +++ b/src/zipper.rs @@ -3923,7 +3923,7 @@ pub(crate) mod zipper_moving_tests { b"romulus", b"rubens", b"ruber", b"rubicon", b"rubicundus", b"rom'i", ]; - pub fn zipper_val_at_test>(mut zipper: Z) { + pub fn zipper_val_at_test>(mut zipper: Z) { assert_eq!(zipper.val_at(b""), None); assert_eq!(zipper.val_at(b"roman"), Some(&())); assert_eq!(zipper.val_at(b"romane"), Some(&())); @@ -3966,7 +3966,7 @@ pub(crate) mod zipper_moving_tests { key } - pub fn zipper_val_at_long_path_test>(mut zipper: Z) { + pub fn zipper_val_at_long_path_test>(mut zipper: Z) { let long_key = zipper_val_at_long_path_test_key(); let relative_long_suffix = &long_key[3..]; let almost_full_suffix = &relative_long_suffix[..relative_long_suffix.len()-1]; From d2d3abc859a65de6983bc78ade86a9afecf8cdac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marcin=20Rze=C5=BAnicki?= Date: Thu, 3 Sep 2026 19:01:55 +0200 Subject: [PATCH 4/4] zipper_algebra: pass a more reasonable child mask --- src/experimental/zipper_algebra.rs | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/src/experimental/zipper_algebra.rs b/src/experimental/zipper_algebra.rs index 325df57a..ddcff966 100644 --- a/src/experimental/zipper_algebra.rs +++ b/src/experimental/zipper_algebra.rs @@ -515,11 +515,11 @@ where Out: ZipperWriting, { if *lhs_grafts != ByteMask::EMPTY { - out.graft_masked_branches(lhs, std::mem::take(lhs_grafts), false); + out.graft_masked_branches(lhs, std::mem::take(lhs_grafts) & lhs.child_mask(), false); } if *rhs_grafts != ByteMask::EMPTY { - out.graft_masked_branches(rhs, std::mem::take(rhs_grafts), false); + out.graft_masked_branches(rhs, std::mem::take(rhs_grafts) & rhs.child_mask(), false); } } @@ -729,15 +729,15 @@ where Out: ZipperWriting, { if *lhs_grafts != ByteMask::EMPTY { - out.graft_masked_branches(lhs, std::mem::take(lhs_grafts), false); + out.graft_masked_branches(lhs, std::mem::take(lhs_grafts) & lhs.child_mask(), false); } if *mid_grafts != ByteMask::EMPTY { - out.graft_masked_branches(mid, std::mem::take(mid_grafts), false); + out.graft_masked_branches(mid, std::mem::take(mid_grafts) & mid.child_mask(), false); } if *rhs_grafts != ByteMask::EMPTY { - out.graft_masked_branches(rhs, std::mem::take(rhs_grafts), false); + out.graft_masked_branches(rhs, std::mem::take(rhs_grafts) & rhs.child_mask(), false); } } @@ -1011,19 +1011,19 @@ fn zipper_merge4( Out: ZipperWriting, { if *z0_grafts != ByteMask::EMPTY { - out.graft_masked_branches(z0, std::mem::take(z0_grafts), false); + out.graft_masked_branches(z0, std::mem::take(z0_grafts) & z0.child_mask(), false); } if *z1_grafts != ByteMask::EMPTY { - out.graft_masked_branches(z1, std::mem::take(z1_grafts), false); + out.graft_masked_branches(z1, std::mem::take(z1_grafts) & z1.child_mask(), false); } if *z2_grafts != ByteMask::EMPTY { - out.graft_masked_branches(z2, std::mem::take(z2_grafts), false); + out.graft_masked_branches(z2, std::mem::take(z2_grafts) & z2.child_mask(), false); } if *z3_grafts != ByteMask::EMPTY { - out.graft_masked_branches(z3, std::mem::take(z3_grafts), false); + out.graft_masked_branches(z3, std::mem::take(z3_grafts) & z3.child_mask(), false); } } @@ -1665,7 +1665,12 @@ where { for_each_bit(active, |i| { if grafts[i] != ByteMask::EMPTY { - out.graft_masked_branches(&zs[i], std::mem::take(&mut grafts[i]), false); + let z = &zs[i]; + out.graft_masked_branches( + z, + std::mem::take(&mut grafts[i]) & z.child_mask(), + false, + ); } }); }