This is an automated email from the ASF dual-hosted git repository.

tlopex pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm-ffi.git


The following commit(s) were added to refs/heads/main by this push:
     new a609ff54 [REFACTOR][Rust] Consolidate structural traversal APIs (#801)
a609ff54 is described below

commit a609ff5493d40a6e85e6f0aa7c8a91b40e9505ab
Author: Shushi Hong <[email protected]>
AuthorDate: Sat Sep 19 15:12:13 2026 -0400

    [REFACTOR][Rust] Consolidate structural traversal APIs (#801)
    
    This PR consolidates the Rust structural traversal APIs, uses
    `MutateContext` for callbacks and policies and `StructuralView` for
    borrowed callback values, and removes redundant aliases and helpers. It
    unifies policy traversal through `structural_walk` and `structural_map`,
    keeps `def_region_kind()` as the region getter, and renames the default
    recursion hook to `on_default_mutate`. It also fixes post-order mapping
    so `Unchanged` preserves child rewrites and moves policy compile-fail
    coverage into inline doctests. Follow-up to
    
[[#800](https://github.com/apache/tvm-ffi/pull/800)](https://github.com/apache/tvm-ffi/pull/800).
---
 docs/guides/rust_lang_guide.md                     |  24 +-
 rust/tvm-ffi-macros/src/dispatch.rs                |  52 +---
 rust/tvm-ffi/src/extra/dispatch.rs                 |   8 +-
 rust/tvm-ffi/src/extra/structural_common.rs        |   1 -
 rust/tvm-ffi/src/extra/structural_mutate.rs        | 312 ++++++---------------
 rust/tvm-ffi/src/extra/structural_mutate/policy.rs |  27 +-
 .../extra/structural_mutate/policy/compile_fail.rs |  50 ----
 rust/tvm-ffi/src/extra/structural_visit.rs         | 152 +++++-----
 rust/tvm-ffi/src/extra/structural_visit/policy.rs  |  27 +-
 rust/tvm-ffi/src/lib.rs                            |  10 +-
 rust/tvm-ffi/tests/test_structural_mutate.rs       | 237 +++++++++-------
 rust/tvm-ffi/tests/test_structural_visit.rs        | 136 +++++----
 .../tests/test_structural_visitor_alignment.rs     |  10 +-
 13 files changed, 429 insertions(+), 617 deletions(-)

diff --git a/docs/guides/rust_lang_guide.md b/docs/guides/rust_lang_guide.md
index 66d64daa..a4b9709d 100644
--- a/docs/guides/rust_lang_guide.md
+++ b/docs/guides/rust_lang_guide.md
@@ -367,8 +367,9 @@ callback must call `visit_children()` explicitly, and 
interrupt values must be
 returned explicitly because `?` only propagates errors.
 
 `VisitCallbacks::with_policy` and `WalkWithContextPolicy` use 
`ContextPolicy<State>`
-to manage context around default recursion. See the `ContextPolicy` API
-documentation for composition and shared state access.
+to manage context around default recursion. Pass `WalkWithContextPolicy` to
+`structural_walk`; pass `&mut walker` to reuse it and inspect its state 
afterward.
+See the `ContextPolicy` API documentation for composition and shared state 
access.
 
 For a named implementation, `#[dispatch(visit)]` generates
 `StructuralVisitor` from `visit_*` methods. Matching handlers own recursion;
@@ -418,7 +419,8 @@ explicitly from a `StructuralVisitor`, or skip it with a 
pre-order
 `WalkResult::Skip`.
 
 `StructuralView` is the shared borrowed callback value for visit, walk, map,
-and mutate; `VisitValue` and `MapValue` remain compatibility names.
+and mutate. Mapping callbacks return replacement values; their input view is
+borrowed and does not grant in-place mutation permission.
 
 ### Structural Mapping and Mutation
 
@@ -491,7 +493,8 @@ the same semantics as C++.
 `MutateCallbacks::with_policy` and `MapWithContextPolicy` use 
`MutContextPolicy<State>`
 to manage context around default recursion. Policies consume `MutateValue`
 and return `UnchangedOr<Any>`; see the API documentation for composition and
-scoped definition regions. Pass `MapWithContextPolicy` directly to 
`structural_map`;
+scoped definition regions. Pass `MapWithContextPolicy` directly to 
`structural_map`,
+or pass `&mut mapper` to reuse it and inspect its state afterward;
 it cannot be a callback tuple member or another wrapper's dispatcher. Compose
 policies as `(outer, inner)` within one wrapper.
 
@@ -510,18 +513,19 @@ not returned on error.
 
 Callbacks can also return `Unchanged` or `UnchangedOr<T>`, optionally wrapped
 in `Result`. A pre-order map still maps the original value's children when
-a callback returns `Unchanged`. Use `mutate_result` and
+a callback returns `Unchanged`; a post-order map keeps the result of child
+mapping. Use `mutate_result` and
 `default_mutate_result` to preserve unchanged during recursion; existing
 owning-value helpers and top-level functions resolve the marker to the
 original value.
 
 `structural_mutate` accepts typed callback chains in addition to a
-`StructuralMutator`. Closure callbacks receive a `CallbackMutator`;
+`StructuralMutator`. Closure callbacks receive a `MutateContext`;
 `MutateCallbacks` adds state shared by that callback chain:
 
 ```rust
 use tvm_ffi::{
-    structural_mutate, Array, CallbackMutator, MutateValue, MutateCallbacks,
+    structural_mutate, Array, MutateCallbacks, MutateContext, MutateValue,
 };
 
 #[derive(Default)]
@@ -532,11 +536,11 @@ struct Stats {
 let mut mutator = MutateCallbacks::new(
     Stats::default(),
     (
-        |value: i64, mutator: &mut CallbackMutator<Stats>| {
+        |value: i64, mutator: &mut MutateContext<'_, Stats>| {
             mutator.state_mut().integers += 1;
             value + 1
         },
-        |value: MutateValue<'_>, mutator: &mut CallbackMutator<Stats>| {
+        |value: MutateValue<'_>, mutator: &mut MutateContext<'_, Stats>| {
             mutator.default_maybe_inplace_mutate(value)
         },
     ),
@@ -547,7 +551,7 @@ assert_eq!(mutated.iter().collect::<Vec<_>>(), vec![2, 3]);
 assert_eq!(mutator.state().integers, 2);
 ```
 
-`CallbackMutator::mutate` uses the copy path for a borrowed value, while
+`MutateContext::mutate` uses the copy path for a borrowed value, while
 `maybe_inplace_mutate` preserves the reuse opportunity of an owned value.
 Closure callbacks are `Fn`; mutable data belongs in the callback state.
 
diff --git a/rust/tvm-ffi-macros/src/dispatch.rs 
b/rust/tvm-ffi-macros/src/dispatch.rs
index feffa55b..b4528d10 100644
--- a/rust/tvm-ffi-macros/src/dispatch.rs
+++ b/rust/tvm-ffi-macros/src/dispatch.rs
@@ -155,10 +155,7 @@ fn expand(item_impl: &ItemImpl, mode: DispatchMode) -> 
syn::Result<TokenStream2>
         DispatchMode::Visit => quote! {
             
#tvm_ffi::extra::structural_visit::IntoVisitResult::into_visit_result
         },
-        DispatchMode::Map => quote! {
-            #tvm_ffi::extra::structural_mutate::IntoMapResult::into_map_result
-        },
-        DispatchMode::Mutate => quote! {
+        DispatchMode::Map | DispatchMode::Mutate => quote! {
             
#tvm_ffi::extra::structural_mutate::IntoMutateResult::into_mutate_result
         },
     };
@@ -198,7 +195,7 @@ fn expand(item_impl: &ItemImpl, mode: DispatchMode) -> 
syn::Result<TokenStream2>
                     &mut self,
                     value: &#tvm_ffi::StructuralView,
                     def_region_kind: 
#tvm_ffi::extra::structural_visit::DefRegionKind,
-                ) -> 
Option<#tvm_ffi::extra::structural_visit::WalkCallbackResult> {
+                ) -> Option<#tvm_ffi::Result<#tvm_ffi::WalkResult>> {
                     #(#links)*
                     None
                 }
@@ -232,7 +229,7 @@ fn expand(item_impl: &ItemImpl, mode: DispatchMode) -> 
syn::Result<TokenStream2>
                     &mut self,
                     value: &#tvm_ffi::StructuralView,
                     def_region_kind: 
#tvm_ffi::extra::structural_visit::DefRegionKind,
-                ) -> Option<#tvm_ffi::extra::structural_mutate::MapResult> {
+                ) -> Option<#tvm_ffi::Result<#tvm_ffi::Any>> {
                     #(#links)*
                     None
                 }
@@ -248,7 +245,7 @@ fn expand(item_impl: &ItemImpl, mode: DispatchMode) -> 
syn::Result<TokenStream2>
                     &mut self,
                     value: &#tvm_ffi::StructuralView,
                     mutator: &mut #tvm_ffi::extra::structural_mutate::Mutator,
-                ) -> Option<#tvm_ffi::extra::structural_mutate::MutateResult> {
+                ) -> Option<#tvm_ffi::Result<#tvm_ffi::Any>> {
                     <Self as 
#tvm_ffi::extra::structural_mutate::MutateDispatch>::dispatch_mutate_value(
                         self, #tvm_ffi::MutateValue::borrowed(value), mutator,
                     )
@@ -260,8 +257,7 @@ fn expand(item_impl: &ItemImpl, mode: DispatchMode) -> 
syn::Result<TokenStream2>
                     &mut self,
                     mut value: #tvm_ffi::MutateValue<'_>,
                     mutator: &mut #tvm_ffi::Mutator,
-                ) -> Option<#tvm_ffi::extra::structural_mutate::MutateResult> {
-                    let inplace_mode = value.inplace_mode();
+                ) -> Option<#tvm_ffi::Result<#tvm_ffi::Any>> {
                     #(#links)*
                     None
                 }
@@ -289,9 +285,6 @@ fn expand_links(
             let method = &handler.method;
             let attrs = &handler.cfg_attrs;
             let trailing_arg = match mode {
-                DispatchMode::Mutate if handler.wants_inplace_mode => {
-                    quote!(, mutator, inplace_mode)
-                }
                 DispatchMode::Mutate if handler.wants_mutator => quote!(, 
mutator),
                 DispatchMode::Mutate => quote!(),
                 _ if handler.wants_def_region => quote!(, def_region_kind),
@@ -365,7 +358,6 @@ struct Handler {
     argument: HandlerArgument,
     wants_def_region: bool,
     wants_mutator: bool,
-    wants_inplace_mode: bool,
     cfg_attrs: Vec<Meta>,
 }
 
@@ -378,21 +370,16 @@ enum HandlerArgument {
 
 fn parse_handler(method: &ImplItemMethod, mode: DispatchMode) -> 
syn::Result<Handler> {
     let inputs = &method.sig.inputs;
-    let receiver_is_expected = match (mode, inputs.first()) {
-        (DispatchMode::Mutate, Some(FnArg::Receiver(receiver))) => {
-            receiver.reference.is_some() && receiver.mutability.is_some()
-        }
-        (_, Some(FnArg::Receiver(receiver))) => {
+    let receiver_is_expected = match inputs.first() {
+        Some(FnArg::Receiver(receiver)) => {
             receiver.reference.is_some() && receiver.mutability.is_some()
         }
         _ => false,
     };
-    let arity_is_expected = inputs.len() == 2
-        || inputs.len() == 3
-        || (matches!(mode, DispatchMode::Mutate) && inputs.len() == 4);
+    let arity_is_expected = inputs.len() == 2 || inputs.len() == 3;
     if !receiver_is_expected || !arity_is_expected {
         let message = if matches!(mode, DispatchMode::Mutate) {
-            "mutate handlers must take `&mut self`, a node, optionally `&mut 
Mutator`, then optionally `InplaceMode`"
+            "mutate handlers must take `&mut self`, a node, and optionally 
`&mut Mutator`"
                 .to_owned()
         } else {
             format!(
@@ -405,16 +392,6 @@ fn parse_handler(method: &ImplItemMethod, mode: 
DispatchMode) -> syn::Result<Han
     }
     let wants_def_region = !matches!(mode, DispatchMode::Mutate) && 
inputs.len() == 3;
     let wants_mutator = matches!(mode, DispatchMode::Mutate) && inputs.len() 
>= 3;
-    let wants_inplace_mode = matches!(mode, DispatchMode::Mutate) && 
inputs.len() == 4;
-    if wants_inplace_mode {
-        let Some(FnArg::Typed(arg)) = inputs.iter().nth(3) else {
-            unreachable!()
-        };
-        if !matches!(arg.ty.as_ref(), Type::Path(path) if 
path.path.segments.last().is_some_and(|s| s.ident == "InplaceMode" && 
matches!(s.arguments, PathArguments::None)))
-        {
-            return Err(syn::Error::new_spanned(&arg.ty, "expected 
`InplaceMode`"));
-        }
-    }
     if wants_mutator {
         let context_type = match inputs.iter().nth(2) {
             Some(FnArg::Typed(context)) => context.ty.as_ref(),
@@ -474,7 +451,6 @@ fn parse_handler(method: &ImplItemMethod, mode: 
DispatchMode) -> syn::Result<Han
         argument,
         wants_def_region,
         wants_mutator,
-        wants_inplace_mode,
         cfg_attrs,
     })
 }
@@ -562,10 +538,8 @@ fn is_dispatch_value(value_type: &Type) -> bool {
     let Type::Path(path) = value_type else {
         return false;
     };
-    path.path.segments.last().is_some_and(|segment| {
-        matches!(
-            segment.ident.to_string().as_str(),
-            "StructuralView" | "VisitValue" | "MapValue"
-        )
-    })
+    path.path
+        .segments
+        .last()
+        .is_some_and(|segment| segment.ident == "StructuralView")
 }
diff --git a/rust/tvm-ffi/src/extra/dispatch.rs 
b/rust/tvm-ffi/src/extra/dispatch.rs
index af6f2a8e..42b263b4 100644
--- a/rust/tvm-ffi/src/extra/dispatch.rs
+++ b/rust/tvm-ffi/src/extra/dispatch.rs
@@ -21,9 +21,7 @@
 
 use crate::error::Result;
 
-use super::structural_visit::{
-    DefRegionKind, IntoWalker, NativeVisit, StructuralView, 
WalkCallbackResult, WalkResult,
-};
+use super::structural_visit::{DefRegionKind, IntoWalker, NativeVisit, 
StructuralView, WalkResult};
 
 /// Dispatch for typed `structural_walk` observer callbacks.
 ///
@@ -34,7 +32,7 @@ pub trait WalkDispatch: Sized {
         &mut self,
         value: &StructuralView,
         def_region_kind: DefRegionKind,
-    ) -> Option<WalkCallbackResult>;
+    ) -> Option<Result<WalkResult>>;
 }
 
 impl<V: WalkDispatch> WalkDispatch for &mut V {
@@ -43,7 +41,7 @@ impl<V: WalkDispatch> WalkDispatch for &mut V {
         &mut self,
         value: &StructuralView,
         def_region_kind: DefRegionKind,
-    ) -> Option<WalkCallbackResult> {
+    ) -> Option<Result<WalkResult>> {
         (**self).dispatch_walk(value, def_region_kind)
     }
 }
diff --git a/rust/tvm-ffi/src/extra/structural_common.rs 
b/rust/tvm-ffi/src/extra/structural_common.rs
index 73242c84..3447692f 100644
--- a/rust/tvm-ffi/src/extra/structural_common.rs
+++ b/rust/tvm-ffi/src/extra/structural_common.rs
@@ -70,7 +70,6 @@ pub(crate) use impl_callback_chain_tuple_arities;
 /// borrows an object node, while [`Self::cast`] returns a typed value 
(acquiring
 /// ownership for object handles). This view does not grant in-place 
permission;
 /// consuming mutation callbacks use [`crate::MutateValue`] instead.
-/// `VisitValue` and `MapValue` remain compatibility names for this type.
 #[repr(transparent)]
 pub struct StructuralView(TVMFFIAny);
 
diff --git a/rust/tvm-ffi/src/extra/structural_mutate.rs 
b/rust/tvm-ffi/src/extra/structural_mutate.rs
index 04b2cfd5..1f656142 100644
--- a/rust/tvm-ffi/src/extra/structural_mutate.rs
+++ b/rust/tvm-ffi/src/extra/structural_mutate.rs
@@ -68,16 +68,9 @@ const FLAG_SETTER_IS_FUNCTION: i64 = 
kTVMFFIFieldFlagBitSetterIsFunctionObj as i
 /// Borrowed value passed to structural map and mutation callbacks.
 pub use super::StructuralView;
 
-/// Compatibility name for [`StructuralView`].
-pub use super::StructuralView as MapValue;
-
 mod policy;
 pub use policy::{DefaultMutContextPolicy, MapWithContextPolicy, 
MutContextPolicy};
 
-/// Result type produced by a structural-map callback.
-#[doc(hidden)]
-pub type MapResult = Result<Any>;
-
 mod callback_result_sealed {
     use super::{Any, Result};
 
@@ -87,27 +80,27 @@ mod callback_result_sealed {
     impl<T: Into<Any>> Sealed for Result<T> {}
 }
 
-/// Convert an infallible or fallible callback result into [`MapResult`].
+/// Convert an infallible or fallible map or mutation callback result into 
[`Result<Any>`].
 ///
 /// A callback may return any value convertible into [`Any`], or wrap it in
 /// [`Result`] to use `?`.
 ///
 /// This trait is sealed and is not an extension point.
 #[doc(hidden)]
-pub trait IntoMapResult: callback_result_sealed::Sealed {
-    fn into_map_result(self) -> MapResult;
+pub trait IntoMutateResult: callback_result_sealed::Sealed {
+    fn into_mutate_result(self) -> Result<Any>;
 }
 
-impl<T: Into<Any>> IntoMapResult for T {
+impl<T: Into<Any>> IntoMutateResult for T {
     #[inline]
-    fn into_map_result(self) -> MapResult {
+    fn into_mutate_result(self) -> Result<Any> {
         Ok(self.into())
     }
 }
 
-impl<T: Into<Any>> IntoMapResult for Result<T> {
+impl<T: Into<Any>> IntoMutateResult for Result<T> {
     #[inline]
-    fn into_map_result(self) -> MapResult {
+    fn into_mutate_result(self) -> Result<Any> {
         self.map(Into::into)
     }
 }
@@ -154,8 +147,8 @@ impl InplaceMode {
 ///
 /// A node borrow cannot survive consumption of the handle:
 /// ```compile_fail
-/// use tvm_ffi::{CallbackMutator, MutateValue};
-/// fn invalid(value: MutateValue<'_>, ctx: &mut CallbackMutator) {
+/// use tvm_ffi::{MutateContext, MutateValue};
+/// fn invalid(value: MutateValue<'_>, ctx: &mut MutateContext<'_>) {
 ///     let node = 
value.as_node::<tvm_ffi::collections::array::ArrayObj>().unwrap();
 ///     ctx.default_maybe_inplace_mutate(value).unwrap();
 ///     println!("{}", node.size);
@@ -165,11 +158,11 @@ impl InplaceMode {
 /// Moving the handle into a nested callback cannot bypass that borrow:
 /// ```compile_fail
 /// use std::cell::RefCell;
-/// use tvm_ffi::{structural_mutate, CallbackMutator, MutateValue};
+/// use tvm_ffi::{structural_mutate, MutateContext, MutateValue};
 /// fn invalid(value: MutateValue<'_>) {
 ///     let node = 
value.as_node::<tvm_ffi::collections::array::ArrayObj>().unwrap();
 ///     let pending = RefCell::new(Some(value));
-///     structural_mutate(true, |_: bool, inner: &mut CallbackMutator| {
+///     structural_mutate(true, |_: bool, inner: &mut MutateContext<'_>| {
 ///         
inner.default_maybe_inplace_mutate(pending.borrow_mut().take().unwrap())
 ///     }).unwrap();
 ///     println!("{}", node.size);
@@ -257,27 +250,19 @@ impl<T> Deref for MutateValue<'_, T> {
     }
 }
 
-/// State and recursive operations available to a callback-chain mutation.
+/// State and recursive operations shared by mutation callbacks and context 
policies.
 ///
 /// A matched callback owns mutation of its value. Recursive operations
 /// reborrow the mutator, so mutable state cannot remain borrowed across them.
 /// The context does not store a node: inspect the callback argument and pass
 /// it explicitly to default recursion.
-pub struct MutateContext<'a, State, Driver: ?Sized = dyn 
MutateContextDriver<State> + 'a> {
-    driver: &'a mut Driver,
+pub struct MutateContext<'a, State = ()> {
+    driver: &'a mut dyn MutateContextDriver<State>,
     def_region_kind: DefRegionKind,
     inplace_mode: InplaceMode,
-    _state: PhantomData<fn() -> State>,
     _not_send_sync: PhantomData<Rc<()>>,
 }
 
-/// Recursive mutation operations passed to closure callback chains.
-///
-/// Typed `#[dispatch(mutate)]` implementations use [`Mutator`] instead and
-/// keep their mutable pass state directly on the dispatch object.
-pub type CallbackMutator<'a, State = (), Driver = dyn 
MutateContextDriver<State> + 'a> =
-    MutateContext<'a, State, Driver>;
-
 /// Recursion control passed to a typed `#[dispatch(mutate)]` handler.
 ///
 /// The dispatch object owns all pass state. Recursive operations take that
@@ -305,12 +290,6 @@ impl Mutator {
         self.def_region_kind
     }
 
-    /// Definition region active at the callback's current value.
-    #[inline(always)]
-    pub fn region(&self) -> DefRegionKind {
-        self.def_region_kind
-    }
-
     /// Mutate a borrowed child through the same typed dispatch object.
     #[inline(always)]
     pub fn mutate<D, T>(&mut self, dispatch: &mut D, value: &T) -> Result<Any>
@@ -386,7 +365,7 @@ impl Mutator {
         D: MutateDispatch,
         for<'x> AnyView<'x>: From<&'x T>,
     {
-        StructuralMutator::default_mutate_value(dispatch, value, 
self.def_region_kind)
+        StructuralMutator::default_mutate(dispatch, value, 
self.def_region_kind)
     }
 
     /// Mutate a borrowed child while preserving an unchanged result.
@@ -425,7 +404,7 @@ impl Mutator {
         D: MutateDispatch,
         for<'x> AnyView<'x>: From<&'x T>,
     {
-        StructuralMutator::default_mutate_value_result(dispatch, value, 
self.def_region_kind)
+        StructuralMutator::default_mutate_result(dispatch, value, 
self.def_region_kind)
     }
 
     /// Consume the handle for default descent with its existing permission.
@@ -500,14 +479,11 @@ impl Mutator {
     }
 }
 
-#[doc(hidden)]
 /// Internal operations used by [`MutateContext`].
 ///
-/// The dispatch macro keeps the concrete implementor visible to the compiler
-/// so recursive `mutate` calls can be inlined. This is not a user extension
-/// point. Borrowed inputs carry a lifetime; in-place entry requires an owned
+/// Borrowed inputs carry a lifetime; in-place entry requires an owned
 /// value or a consumed capability, never a bare ABI value plus permission.
-pub trait MutateContextDriver<State> {
+trait MutateContextDriver<State> {
     fn state(&self) -> &State;
     fn state_mut(&mut self) -> &mut State;
     fn mutate_borrowed(&mut self, value: AnyView<'_>, kind: DefRegionKind) -> 
Result<Any>;
@@ -526,10 +502,7 @@ pub trait MutateContextDriver<State> {
     fn var_remap_set(&mut self, var: &StructuralView, mutated_value: &Any) -> 
Result<()>;
 }
 
-impl<State, Driver> MutateContext<'_, State, Driver>
-where
-    Driver: MutateContextDriver<State> + ?Sized,
-{
+impl<State> MutateContext<'_, State> {
     /// User state shared by every callback in this mutation.
     #[inline(always)]
     pub fn state(&self) -> &State {
@@ -556,25 +529,18 @@ where
         self.def_region_kind
     }
 
-    /// Definition region active at the callback's current value.
-    #[inline]
-    pub fn region(&self) -> DefRegionKind {
-        self.def_region_kind
-    }
-
     /// Run recursive operations in a definition region, preserving an outer 
Pattern.
     /// The previous context is restored on return, error, or unwinding.
     pub fn with_def_region_kind<T>(
         &mut self,
         kind: DefRegionKind,
-        callback: impl FnOnce(&mut MutateContext<'_, State, Driver>) -> 
Result<T>,
+        callback: impl FnOnce(&mut MutateContext<'_, State>) -> Result<T>,
     ) -> Result<T> {
         with_mutation_region(kind, |kind| {
             callback(&mut MutateContext {
                 driver: &mut *self.driver,
                 def_region_kind: kind,
                 inplace_mode: self.inplace_mode,
-                _state: PhantomData,
                 _not_send_sync: PhantomData,
             })
         })
@@ -751,7 +717,7 @@ where
 /// remains available for closure callback chains with separate state.
 #[diagnostic::on_unimplemented(
     message = "`{Self}` is not a supported `structural_mutate` mutator",
-    note = "accepted mutators: `&mut U` where `U: StructuralMutator`; a 
generated `MutateDispatch`; an `Fn` callback over an FFI value type `T`, `&N` 
of an object node type, or `&StructuralView`, or a consuming `MutateValue<T>`, 
followed by `&mut CallbackMutator<State>`; or a tuple of up to 12 such 
callbacks (tuples may nest)",
+    note = "accepted mutators: `&mut U` where `U: StructuralMutator`; a 
generated `MutateDispatch`; an `Fn` callback over an FFI value type `T`, `&N` 
of an object node type, or `&StructuralView`, or a consuming `MutateValue<T>`, 
followed by `&mut MutateContext<'_, State>`; or a tuple of up to 12 such 
callbacks (tuples may nest)",
     note = "callback arguments need explicit type annotations; use 
`MutateCallbacks::new(state, callbacks)` for ordinary mutable callback state"
 )]
 pub trait IntoMutator<Marker> {
@@ -765,47 +731,14 @@ impl<U: StructuralMutator> IntoMutator<U> for &mut U {
     }
 }
 
-/// Convert a mutation callback result into [`Result<Any>`].
-///
-/// A callback may return any value convertible into [`Any`], or wrap it in
-/// [`Result`] to use `?`.
-#[doc(hidden)]
-pub trait IntoMutateResult: callback_result_sealed::Sealed {
-    fn into_mutate_result(self) -> Result<Any>;
-}
-
-impl<T: Into<Any>> IntoMutateResult for T {
-    #[inline]
-    fn into_mutate_result(self) -> Result<Any> {
-        Ok(self.into())
-    }
-}
-
-impl<T: Into<Any>> IntoMutateResult for Result<T> {
-    #[inline]
-    fn into_mutate_result(self) -> Result<Any> {
-        self.map(Into::into)
-    }
-}
-
-#[doc(hidden)]
-pub type MutateResult = Result<Any>;
-
-#[doc(hidden)]
-/// Callback tuples use a type-erased mutation driver.
-pub enum DynamicMutateCallbacks {}
-
 /// One typed callback in a callback-driven structural mutator.
 pub trait MutateChainLink<State, Marker>: mutate_sealed::SealedLink<State, 
Marker> {
-    #[doc(hidden)]
-    type Strategy;
-
     #[doc(hidden)]
     fn try_mutate(
         &self,
         value: &mut Option<MutateValue<'_>>,
         mutator: &mut MutateContext<'_, State>,
-    ) -> Option<MutateResult>;
+    ) -> Option<Result<Any>>;
 }
 
 /// Ordered typed callback dispatch for [`structural_mutate`].
@@ -816,13 +749,13 @@ pub trait MutateChainLink<State, Marker>: 
mutate_sealed::SealedLink<State, Marke
 /// first match. The implementation owns its pass state and receives `&mut
 /// self`, while [`Mutator`] controls recursion, the definition region, and
 /// in-place permission. Handlers can consume [`MutateValue`] and take an
-/// optional trailing [`InplaceMode`] after `&mut Mutator`.
+/// optional `&mut Mutator` to inspect the region and in-place permission.
 pub trait MutateDispatch: Sized {
     fn dispatch_mutate(
         &mut self,
         value: &StructuralView,
         mutator: &mut Mutator,
-    ) -> Option<MutateResult>;
+    ) -> Option<Result<Any>>;
 
     /// Dispatch an engine-issued value. Existing borrowed dispatchers retain
     /// their copy-only default recursion; generated dispatch supports 
consuming it.
@@ -830,7 +763,7 @@ pub trait MutateDispatch: Sized {
         &mut self,
         value: MutateValue<'_>,
         mutator: &mut Mutator,
-    ) -> Option<MutateResult> {
+    ) -> Option<Result<Any>> {
         self.dispatch_mutate(value.as_value(), mutator)
     }
 }
@@ -900,12 +833,11 @@ where
     T: crate::type_traits::ContainerElement,
     O: IntoMutateResult,
 {
-    type Strategy = DynamicMutateCallbacks;
     fn try_mutate(
         &self,
         value: &mut Option<MutateValue<'_>>,
         mutator: &mut MutateContext<'_, State>,
-    ) -> Option<MutateResult> {
+    ) -> Option<Result<Any>> {
         match value
             .take()
             .expect("unconsumed callback value")
@@ -929,13 +861,11 @@ where
     T: crate::type_traits::AnyCompatible,
     O: IntoMutateResult,
 {
-    type Strategy = DynamicMutateCallbacks;
-
     fn try_mutate(
         &self,
         value: &mut Option<MutateValue<'_>>,
         mutator: &mut MutateContext<'_, State>,
-    ) -> Option<MutateResult> {
+    ) -> Option<Result<Any>> {
         value
             .as_ref()
             .expect("unconsumed callback value")
@@ -956,13 +886,11 @@ where
     N: ObjectCore,
     O: IntoMutateResult,
 {
-    type Strategy = DynamicMutateCallbacks;
-
     fn try_mutate(
         &self,
         value: &mut Option<MutateValue<'_>>,
         mutator: &mut MutateContext<'_, State>,
-    ) -> Option<MutateResult> {
+    ) -> Option<Result<Any>> {
         value
             .as_ref()
             .expect("unconsumed callback value")
@@ -982,13 +910,11 @@ where
     ) -> O,
     O: IntoMutateResult,
 {
-    type Strategy = DynamicMutateCallbacks;
-
     fn try_mutate(
         &self,
         value: &mut Option<MutateValue<'_>>,
         mutator: &mut MutateContext<'_, State>,
-    ) -> Option<MutateResult> {
+    ) -> Option<Result<Any>> {
         Some(
             self(
                 value
@@ -1019,13 +945,11 @@ macro_rules! impl_mutate_chain_link {
         where
             $($F: MutateChainLink<State, $M>,)+
         {
-            type Strategy = DynamicMutateCallbacks;
-
             fn try_mutate(
                 &self,
                 value: &mut Option<MutateValue<'_>>,
                 mutator: &mut MutateContext<'_, State>,
-            ) -> Option<MutateResult> {
+            ) -> Option<Result<Any>> {
                 $(
                     if let Some(result) = self.$idx.try_mutate(value, mutator) 
{
                         return Some(result);
@@ -1131,7 +1055,6 @@ pub struct 
ByMutateCallbacks<Marker>(PhantomData<fn(Marker)>);
 impl<Link, Marker> IntoMutator<ByMutateCallbacks<Marker>> for Link
 where
     Link: MutateChainLink<(), Marker>,
-    Link::Strategy: MutateCallbackStrategy<(), Link, Marker>,
 {
     fn mutate_root(self, root: Any) -> Result<Any> {
         let callbacks = self;
@@ -1154,7 +1077,7 @@ pub trait MapDispatch: Sized {
         &mut self,
         value: &StructuralView,
         def_region_kind: DefRegionKind,
-    ) -> Option<MapResult>;
+    ) -> Option<Result<Any>>;
 }
 
 /// Internal root-mapping protocol used by [`IntoMapper`].
@@ -1183,7 +1106,7 @@ impl<V: MapDispatch> MapDispatch for &mut V {
         &mut self,
         value: &StructuralView,
         def_region_kind: DefRegionKind,
-    ) -> Option<MapResult> {
+    ) -> Option<Result<Any>> {
         (**self).dispatch_map(value, def_region_kind)
     }
 }
@@ -1221,53 +1144,53 @@ pub trait MapChainLink<Marker>: 
sealed_map::SealedMapLink<Marker> {
         &mut self,
         value: &StructuralView,
         def_region_kind: DefRegionKind,
-    ) -> Option<MapResult>;
+    ) -> Option<Result<Any>>;
 }
 
 mod sealed_map {
-    use super::{DefRegionKind, IntoMapResult, MapDispatch, ObjectCore, 
StructuralView};
+    use super::{DefRegionKind, IntoMutateResult, MapDispatch, ObjectCore, 
StructuralView};
 
     pub trait SealedMapLink<Marker> {}
 
     impl<F, T, O> SealedMapLink<super::ByMapOwned<T>> for F
     where
         F: FnMut(T) -> O,
-        O: IntoMapResult,
+        O: IntoMutateResult,
     {
     }
 
     impl<F, T, O> SealedMapLink<super::ByMapOwnedKind<T>> for F
     where
         F: FnMut(T, DefRegionKind) -> O,
-        O: IntoMapResult,
+        O: IntoMutateResult,
     {
     }
 
     impl<F, N: ObjectCore, O> SealedMapLink<super::ByMapNode<N>> for F
     where
         F: for<'a> FnMut(&'a N) -> O,
-        O: IntoMapResult,
+        O: IntoMutateResult,
     {
     }
 
     impl<F, N: ObjectCore, O> SealedMapLink<super::ByMapNodeKind<N>> for F
     where
         F: for<'a> FnMut(&'a N, DefRegionKind) -> O,
-        O: IntoMapResult,
+        O: IntoMutateResult,
     {
     }
 
     impl<F, O> SealedMapLink<super::ByMapCatchAll> for F
     where
         F: for<'a> FnMut(&'a StructuralView) -> O,
-        O: IntoMapResult,
+        O: IntoMutateResult,
     {
     }
 
     impl<F, O> SealedMapLink<super::ByMapCatchAllKind> for F
     where
         F: for<'a> FnMut(&'a StructuralView, DefRegionKind) -> O,
-        O: IntoMapResult,
+        O: IntoMutateResult,
     {
     }
 
@@ -1281,15 +1204,17 @@ impl<F, T, O> MapChainLink<ByMapOwned<T>> for F
 where
     F: FnMut(T) -> O,
     T: crate::type_traits::AnyCompatible,
-    O: IntoMapResult,
+    O: IntoMutateResult,
 {
     #[inline]
     fn try_map(
         &mut self,
         value: &StructuralView,
         _def_region_kind: DefRegionKind,
-    ) -> Option<MapResult> {
-        value.cast::<T>().map(|typed| self(typed).into_map_result())
+    ) -> Option<Result<Any>> {
+        value
+            .cast::<T>()
+            .map(|typed| self(typed).into_mutate_result())
     }
 }
 
@@ -1300,17 +1225,17 @@ impl<F, T, O> MapChainLink<ByMapOwnedKind<T>> for F
 where
     F: FnMut(T, DefRegionKind) -> O,
     T: crate::type_traits::AnyCompatible,
-    O: IntoMapResult,
+    O: IntoMutateResult,
 {
     #[inline]
     fn try_map(
         &mut self,
         value: &StructuralView,
         def_region_kind: DefRegionKind,
-    ) -> Option<MapResult> {
+    ) -> Option<Result<Any>> {
         value
             .cast::<T>()
-            .map(|typed| self(typed, def_region_kind).into_map_result())
+            .map(|typed| self(typed, def_region_kind).into_mutate_result())
     }
 }
 
@@ -1321,17 +1246,17 @@ impl<F, N, O> MapChainLink<ByMapNode<N>> for F
 where
     F: for<'a> FnMut(&'a N) -> O,
     N: ObjectCore,
-    O: IntoMapResult,
+    O: IntoMutateResult,
 {
     #[inline]
     fn try_map(
         &mut self,
         value: &StructuralView,
         _def_region_kind: DefRegionKind,
-    ) -> Option<MapResult> {
+    ) -> Option<Result<Any>> {
         value
             .as_node::<N>()
-            .map(|node| self(node).into_map_result())
+            .map(|node| self(node).into_mutate_result())
     }
 }
 
@@ -1342,17 +1267,17 @@ impl<F, N, O> MapChainLink<ByMapNodeKind<N>> for F
 where
     F: for<'a> FnMut(&'a N, DefRegionKind) -> O,
     N: ObjectCore,
-    O: IntoMapResult,
+    O: IntoMutateResult,
 {
     #[inline]
     fn try_map(
         &mut self,
         value: &StructuralView,
         def_region_kind: DefRegionKind,
-    ) -> Option<MapResult> {
+    ) -> Option<Result<Any>> {
         value
             .as_node::<N>()
-            .map(|node| self(node, def_region_kind).into_map_result())
+            .map(|node| self(node, def_region_kind).into_mutate_result())
     }
 }
 
@@ -1362,15 +1287,15 @@ pub enum ByMapCatchAll {}
 impl<F, O> MapChainLink<ByMapCatchAll> for F
 where
     F: for<'a> FnMut(&'a StructuralView) -> O,
-    O: IntoMapResult,
+    O: IntoMutateResult,
 {
     #[inline]
     fn try_map(
         &mut self,
         value: &StructuralView,
         _def_region_kind: DefRegionKind,
-    ) -> Option<MapResult> {
-        Some(self(value).into_map_result())
+    ) -> Option<Result<Any>> {
+        Some(self(value).into_mutate_result())
     }
 }
 
@@ -1380,15 +1305,15 @@ pub enum ByMapCatchAllKind {}
 impl<F, O> MapChainLink<ByMapCatchAllKind> for F
 where
     F: for<'a> FnMut(&'a StructuralView, DefRegionKind) -> O,
-    O: IntoMapResult,
+    O: IntoMutateResult,
 {
     #[inline]
     fn try_map(
         &mut self,
         value: &StructuralView,
         def_region_kind: DefRegionKind,
-    ) -> Option<MapResult> {
-        Some(self(value, def_region_kind).into_map_result())
+    ) -> Option<Result<Any>> {
+        Some(self(value, def_region_kind).into_mutate_result())
     }
 }
 
@@ -1404,7 +1329,7 @@ impl<V: MapDispatch> MapChainLink<ByMapDispatchLink> for 
&mut V {
         &mut self,
         value: &StructuralView,
         def_region_kind: DefRegionKind,
-    ) -> Option<MapResult> {
+    ) -> Option<Result<Any>> {
         self.dispatch_map(value, def_region_kind)
     }
 }
@@ -1435,7 +1360,7 @@ where
         &mut self,
         value: &StructuralView,
         def_region_kind: DefRegionKind,
-    ) -> Option<MapResult> {
+    ) -> Option<Result<Any>> {
         self.link.try_map(value, def_region_kind)
     }
 }
@@ -1457,7 +1382,7 @@ macro_rules! impl_map_chain_link {
                 &mut self,
                 value: &StructuralView,
                 def_region_kind: DefRegionKind,
-            ) -> Option<MapResult> {
+            ) -> Option<Result<Any>> {
                 $(
                     if let Some(result) = self.$idx.try_map(value, 
def_region_kind) {
                         return Some(result);
@@ -1490,7 +1415,7 @@ macro_rules! impl_bare_map_link {
             where
                 F: FnMut($($fn_args),+) -> O,
                 Self: MapChainLink<$marker<T>>,
-                O: IntoMapResult,
+                O: IntoMutateResult,
             {
                 type Mapper = MapChain<F, $marker<T>>;
 
@@ -1513,7 +1438,7 @@ impl_bare_map_link!(
 impl<F, O> IntoMapper<ByMapCatchAll> for F
 where
     F: for<'a> FnMut(&'a StructuralView) -> O,
-    O: IntoMapResult,
+    O: IntoMutateResult,
 {
     type Mapper = MapChain<F, ByMapCatchAll>;
 
@@ -1526,7 +1451,7 @@ where
 impl<F, O> IntoMapper<ByMapCatchAllKind> for F
 where
     F: for<'a> FnMut(&'a StructuralView, DefRegionKind) -> O,
-    O: IntoMapResult,
+    O: IntoMutateResult,
 {
     type Mapper = MapChain<F, ByMapCatchAllKind>;
 
@@ -1641,11 +1566,7 @@ pub trait StructuralMutator: Sized {
     }
 
     #[doc(hidden)]
-    fn dispatch_default_mutate(
-        &mut self,
-        value: MutateValue<'_>,
-        kind: DefRegionKind,
-    ) -> Result<Any> {
+    fn on_default_mutate(&mut self, value: MutateValue<'_>, kind: 
DefRegionKind) -> Result<Any> {
         default_mutate_driver(
             self,
             value.value.raw(),
@@ -1698,23 +1619,13 @@ pub trait StructuralMutator: Sized {
         Ok(if is_unchanged(&result) { value } else { result })
     }
 
-    /// Apply default non-in-place mutation to `value`'s children.
-    fn default_mutate(
-        &mut self,
-        value: &StructuralView,
-        def_region_kind: DefRegionKind,
-    ) -> Result<Any> {
-        user_default_mutate(self, value.raw(), def_region_kind, Permit::Copy)
-            .and_then(|result| resolve_result(result, value.raw()))
-    }
-
     /// Apply default non-in-place mutation to a borrowed typed value.
     ///
     /// Unlike [Self::mutate], this bypasses dispatch for the value
     /// itself while its children still re-enter this mutator. This lets a
     /// typed structural-mutate handler recurse through its current node
     /// before applying a post-order rewrite.
-    fn default_mutate_value<T>(&mut self, value: &T, def_region_kind: 
DefRegionKind) -> Result<Any>
+    fn default_mutate<T>(&mut self, value: &T, def_region_kind: DefRegionKind) 
-> Result<Any>
     where
         for<'x> AnyView<'x>: From<&'x T>,
     {
@@ -1764,18 +1675,8 @@ pub trait StructuralMutator: Sized {
             .and_then(UnchangedOr::from_carrier)
     }
 
-    /// Default non-in-place mutation with an unchanged-or-replacement result.
-    fn default_mutate_result(
-        &mut self,
-        value: &StructuralView,
-        kind: DefRegionKind,
-    ) -> Result<UnchangedOr<Any>> {
-        user_default_mutate(self, value.raw(), kind, Permit::Copy)
-            .and_then(UnchangedOr::from_carrier)
-    }
-
     /// Default mutation of a borrowed typed value, preserving unchanged.
-    fn default_mutate_value_result<T>(
+    fn default_mutate_result<T>(
         &mut self,
         value: &T,
         kind: DefRegionKind,
@@ -1876,51 +1777,6 @@ impl<D: MutateDispatch> StructuralMutator for D {
 
 // Closure callback chains use a type-erased context driver so one concrete
 // function signature can recurse through the complete chain.
-trait MutateCallbackStrategy<State, Link, Marker> {
-    fn try_mutate<Driver>(
-        driver: &mut Driver,
-        callback_ptr: *const Link,
-        value: &StructuralView,
-        def_region_kind: DefRegionKind,
-        inplace_mode: InplaceMode,
-    ) -> Option<MutateResult>
-    where
-        Driver: MutateContextDriver<State>;
-}
-
-impl<State, Link, Marker> MutateCallbackStrategy<State, Link, Marker> for 
DynamicMutateCallbacks
-where
-    Link: MutateChainLink<State, Marker>,
-{
-    #[inline(always)]
-    fn try_mutate<Driver>(
-        driver: &mut Driver,
-        callback_ptr: *const Link,
-        value: &StructuralView,
-        def_region_kind: DefRegionKind,
-        inplace_mode: InplaceMode,
-    ) -> Option<MutateResult>
-    where
-        Driver: MutateContextDriver<State>,
-    {
-        let mut mutator = MutateContext::<State, dyn 
MutateContextDriver<State>> {
-            driver,
-            def_region_kind,
-            inplace_mode,
-            _state: PhantomData,
-            _not_send_sync: PhantomData,
-        };
-        // SAFETY: The owning `Rc` or the direct callback's stack slot remains 
live
-        // and is never modified through the driver during recursive reentry.
-        unsafe {
-            (&*callback_ptr).try_mutate(
-                &mut Some(MutateValue::new(value, inplace_mode)),
-                &mut mutator,
-            )
-        }
-    }
-}
-
 #[inline(always)]
 fn try_mutate_callbacks<State, Link, Marker, Driver>(
     driver: &mut Driver,
@@ -1928,26 +1784,31 @@ fn try_mutate_callbacks<State, Link, Marker, Driver>(
     value: &StructuralView,
     def_region_kind: DefRegionKind,
     inplace_mode: InplaceMode,
-) -> Option<MutateResult>
+) -> Option<Result<Any>>
 where
     Link: MutateChainLink<State, Marker>,
-    Link::Strategy: MutateCallbackStrategy<State, Link, Marker>,
     Driver: MutateContextDriver<State>,
 {
-    <Link::Strategy as MutateCallbackStrategy<State, Link, 
Marker>>::try_mutate(
+    let mut mutator = MutateContext {
         driver,
-        callback_ptr,
-        value,
         def_region_kind,
         inplace_mode,
-    )
+        _not_send_sync: PhantomData,
+    };
+    // SAFETY: The owning `Rc` or the direct callback's stack slot remains live
+    // and is never modified through the driver during recursive reentry.
+    unsafe {
+        (&*callback_ptr).try_mutate(
+            &mut Some(MutateValue::new(value, inplace_mode)),
+            &mut mutator,
+        )
+    }
 }
 
 impl<State, Link, Marker, Policy> StructuralMutator for MutateCallbacks<State, 
Link, Marker, Policy>
 where
     Policy: MutContextPolicy<State>,
     Link: MutateChainLink<State, Marker>,
-    Link::Strategy: MutateCallbackStrategy<State, Link, Marker>,
 {
     #[inline(always)]
     fn dispatch_mutate(
@@ -1989,11 +1850,7 @@ where
         }
     }
 
-    fn dispatch_default_mutate(
-        &mut self,
-        value: MutateValue<'_>,
-        kind: DefRegionKind,
-    ) -> Result<Any> {
+    fn on_default_mutate(&mut self, value: MutateValue<'_>, kind: 
DefRegionKind) -> Result<Any> {
         match self.policy.clone() {
             Some(policy) => policy::mutate_with_policy(
                 &mut policy::MutationDescent { driver: self },
@@ -2014,7 +1871,6 @@ where
 impl<Link, Marker> StructuralMutator for DirectMutateCallbacks<'_, Link, 
Marker>
 where
     Link: MutateChainLink<(), Marker>,
-    Link::Strategy: MutateCallbackStrategy<(), Link, Marker>,
 {
     #[inline(always)]
     fn dispatch_mutate(
@@ -2218,7 +2074,9 @@ impl<D: MapDispatch, Policy: MutContextPolicy<D>> 
NativeMapper<'_, D, Policy> {
                     *mapped.as_raw_ffi_any()
                 };
                 let value = StructuralView::from_raw(mapped_raw);
+                // Keep child rewrites when the callback leaves its input 
unchanged.
                 match self.dispatch.dispatch_map(&value, def_region_kind) {
+                    Some(Ok(result)) if is_unchanged(&result) => Ok(mapped),
                     Some(result) => result,
                     None => Ok(mapped),
                 }
@@ -3118,7 +2976,7 @@ fn user_default_mutate<U: StructuralMutator>(
 ) -> Result<Any> {
     with_mutation_region(def_region_kind, |kind| {
         let value = StructuralView::from_raw(raw);
-        mutator.dispatch_default_mutate(MutateValue::new(&value, 
permit.inplace_mode(raw)), kind)
+        mutator.on_default_mutate(MutateValue::new(&value, 
permit.inplace_mode(raw)), kind)
     })
 }
 
diff --git a/rust/tvm-ffi/src/extra/structural_mutate/policy.rs 
b/rust/tvm-ffi/src/extra/structural_mutate/policy.rs
index 1ebe7a0d..1d9cdd97 100644
--- a/rust/tvm-ffi/src/extra/structural_mutate/policy.rs
+++ b/rust/tvm-ffi/src/extra/structural_mutate/policy.rs
@@ -21,9 +21,6 @@
 
 use super::*;
 
-#[cfg(doctest)]
-mod compile_fail;
-
 /// Default-recursion policy for [`MutateCallbacks::with_policy`] and 
[`MapWithContextPolicy`].
 ///
 /// `ctx.default_maybe_inplace_mutate_result(value)` continues to the next 
policy,
@@ -86,7 +83,6 @@ pub(super) fn mutate_with_policy<State>(
             driver,
             def_region_kind: kind,
             inplace_mode: value.inplace_mode(),
-            _state: PhantomData,
             _not_send_sync: PhantomData,
         };
         policy.default_mutate(value, &mut ctx).map(Any::from)
@@ -195,8 +191,8 @@ impl<D, Policy> MutateCallbackState<D> for NativeMapper<'_, 
D, Policy> {
 
 /// A [`MapDispatch`] with a [`MutContextPolicy`], sharing the dispatcher's 
state.
 ///
-/// Run with [`Self::map`] or [`structural_map`]. Each node's map callback runs
-/// outside its policy scope; its children run inside.
+/// Pass this value or a mutable reference to [`structural_map`]. Each node's
+/// map callback runs outside its policy scope; its children run inside.
 /// This mapper cannot be a callback tuple member or another wrapper's 
dispatcher.
 /// Compose policies as `(outer, inner)` within one wrapper.
 pub struct MapWithContextPolicy<Mapper, Policy> {
@@ -227,11 +223,14 @@ impl<Mapper: MapDispatch, Policy: 
MutContextPolicy<Mapper>> MapWithContextPolicy
     pub fn into_state(self) -> Mapper {
         self.mapper
     }
+}
 
-    /// Map an owning root with fresh invocation-local identity substitutions.
-    pub fn map<R: Into<Any>>(&mut self, root: R, order: WalkOrder) -> 
Result<Any> {
+impl<Mapper: MapDispatch, Policy: MutContextPolicy<Mapper>> NativeMap
+    for MapWithContextPolicy<Mapper, Policy>
+{
+    fn map_root(&mut self, root: Any, order: WalkOrder) -> Result<Any> {
         run_structural_mutator(
-            root.into(),
+            root,
             &mut NativeMapper {
                 dispatch: &mut self.mapper,
                 order,
@@ -242,19 +241,11 @@ impl<Mapper: MapDispatch, Policy: 
MutContextPolicy<Mapper>> MapWithContextPolicy
     }
 }
 
-impl<Mapper: MapDispatch, Policy: MutContextPolicy<Mapper>> NativeMap
-    for MapWithContextPolicy<Mapper, Policy>
-{
-    fn map_root(&mut self, root: Any, order: WalkOrder) -> Result<Any> {
-        self.map(root, order)
-    }
-}
-
 impl<Mapper: MapDispatch, Policy: MutContextPolicy<Mapper>> NativeMap
     for &mut MapWithContextPolicy<Mapper, Policy>
 {
     fn map_root(&mut self, root: Any, order: WalkOrder) -> Result<Any> {
-        self.map(root, order)
+        (**self).map_root(root, order)
     }
 }
 
diff --git a/rust/tvm-ffi/src/extra/structural_mutate/policy/compile_fail.rs 
b/rust/tvm-ffi/src/extra/structural_mutate/policy/compile_fail.rs
deleted file mode 100644
index 16bb79be..00000000
--- a/rust/tvm-ffi/src/extra/structural_mutate/policy/compile_fail.rs
+++ /dev/null
@@ -1,50 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-//! A policy mapper cannot be used as a callback, even in a one-element tuple.
-//! ```compile_fail,E0277
-//! use tvm_ffi::*;
-//! fn invalid<D: MapDispatch>(mapper: &mut MapWithContextPolicy<D, 
DefaultMutContextPolicy>) {
-//!     structural_map(1_i64, (mapper,), WalkOrder::PreOrder).unwrap();
-//! }
-//! ```
-//!
-//! Adding another callback must not discard the policy.
-//! ```compile_fail,E0277
-//! use tvm_ffi::*;
-//! fn invalid<D: MapDispatch>(mapper: &mut MapWithContextPolicy<D, 
DefaultMutContextPolicy>) {
-//!     structural_map(1_i64, (mapper, |s: String| s), 
WalkOrder::PreOrder).unwrap();
-//! }
-//! ```
-//!
-//! Nested callback tuples obey the same restriction.
-//! ```compile_fail,E0277
-//! use tvm_ffi::*;
-//! fn invalid<D: MapDispatch>(mapper: &mut MapWithContextPolicy<D, 
DefaultMutContextPolicy>) {
-//!     structural_map(1_i64, (|s: String| s, ((mapper,),)), 
WalkOrder::PostOrder).unwrap();
-//! }
-//! ```
-//!
-//! Compose policies in a tuple instead of nesting mapper wrappers.
-//! ```compile_fail,E0277
-//! use tvm_ffi::*;
-//! fn invalid<D: MapDispatch>(mapper: MapWithContextPolicy<D, 
DefaultMutContextPolicy>) {
-//!     MapWithContextPolicy::new(mapper, DefaultMutContextPolicy);
-//! }
-//! ```
diff --git a/rust/tvm-ffi/src/extra/structural_visit.rs 
b/rust/tvm-ffi/src/extra/structural_visit.rs
index eedce47c..1ac54421 100644
--- a/rust/tvm-ffi/src/extra/structural_visit.rs
+++ b/rust/tvm-ffi/src/extra/structural_visit.rs
@@ -200,15 +200,8 @@ impl IntoVisitResult for Result<Option<VisitInterrupt>> {
     }
 }
 
-/// Fallible result returned by generated typed dispatch.
-#[doc(hidden)]
-pub type WalkCallbackResult = Result<WalkResult>;
-
 pub use super::StructuralView;
 
-/// Compatibility name for [`StructuralView`].
-pub use super::StructuralView as VisitValue;
-
 enum NativeHalt {
     Interrupt(Any),
     Error(Error),
@@ -623,14 +616,16 @@ pub use super::dispatch::{ByWalkDispatch, DispatchWalker, 
WalkDispatch};
 
 /// Conversion into the walker argument of [`structural_walk`].
 ///
-/// Accepts a mutable [`WalkDispatch`], a typed callback, or a nested callback
-/// tuple. `Marker` distinguishes the supported callback shapes.
+/// Accepts a mutable [`WalkDispatch`], a typed callback, a nested callback
+/// tuple, or an owned or borrowed [`WalkWithContextPolicy`].
+/// `Marker` distinguishes the supported callback shapes.
 #[diagnostic::on_unimplemented(
     message = "`{Self}` is not a supported `structural_walk` walker",
     note = "accepted walkers: `&mut V` where `V: WalkDispatch`; a closure over 
`&StructuralView`, \
             an FFI value type `T`, or `&N` of an object node type (`N: 
ObjectCore`, e.g. \
             `&Object`), optionally with a trailing `DefRegionKind` argument; 
or a tuple of \
-            up to 12 such links (tuples nest, so `(a, (b, c))` chains more)",
+            up to 12 such links (tuples nest, so `(a, (b, c))` chains more); \
+            or a `WalkWithContextPolicy` passed by value or mutable reference",
     note = "closure arguments need explicit type annotations; ObjectRef 
wrappers like `String` \
             or `Array<T>` are FFI value types — take them by value, not by 
reference"
 )]
@@ -641,71 +636,25 @@ pub trait IntoWalker<Marker> {
     fn into_walker(self) -> Self::Walker;
 }
 
-/// Adapter for a catch-all walk callback.
-#[doc(hidden)]
-pub struct ClosureWalker<F> {
-    callback: F,
-}
-
-impl<F, O> NativeVisit for ClosureWalker<F>
-where
-    F: FnMut(&StructuralView) -> O,
-    O: IntoWalkResult,
-{
-    fn visit(
-        &mut self,
-        value: &StructuralView,
-        _def_region_kind: DefRegionKind,
-    ) -> Result<WalkResult> {
-        (self.callback)(value).into_walk_result()
-    }
-}
-
-#[doc(hidden)]
-pub enum ByValueClosure {}
-
-impl<F, O> IntoWalker<ByValueClosure> for F
+impl<F, O> IntoWalker<ByCatchAllLink> for F
 where
-    F: FnMut(&StructuralView) -> O,
+    F: for<'a> FnMut(&'a StructuralView) -> O,
     O: IntoWalkResult,
 {
-    type Walker = ClosureWalker<F>;
+    type Walker = ChainWalker<F, ByCatchAllLink>;
     fn into_walker(self) -> Self::Walker {
-        ClosureWalker { callback: self }
+        ChainWalker::new(self)
     }
 }
 
-/// Catch-all walk adapter that also supplies the definition-region state.
-#[doc(hidden)]
-pub struct ClosureKindWalker<F> {
-    callback: F,
-}
-
-impl<F, O> NativeVisit for ClosureKindWalker<F>
+impl<F, O> IntoWalker<ByCatchAllKindLink> for F
 where
-    F: FnMut(&StructuralView, DefRegionKind) -> O,
-    O: IntoWalkResult,
-{
-    fn visit(
-        &mut self,
-        value: &StructuralView,
-        def_region_kind: DefRegionKind,
-    ) -> Result<WalkResult> {
-        (self.callback)(value, def_region_kind).into_walk_result()
-    }
-}
-
-#[doc(hidden)]
-pub enum ByValueKindClosure {}
-
-impl<F, O> IntoWalker<ByValueKindClosure> for F
-where
-    F: FnMut(&StructuralView, DefRegionKind) -> O,
+    F: for<'a> FnMut(&'a StructuralView, DefRegionKind) -> O,
     O: IntoWalkResult,
 {
-    type Walker = ClosureKindWalker<F>;
+    type Walker = ChainWalker<F, ByCatchAllKindLink>;
     fn into_walker(self) -> Self::Walker {
-        ClosureKindWalker { callback: self }
+        ChainWalker::new(self)
     }
 }
 
@@ -722,7 +671,7 @@ pub trait WalkChainLink<Marker>: sealed::SealedLink<Marker> 
{
         &mut self,
         value: &StructuralView,
         def_region_kind: DefRegionKind,
-    ) -> Option<WalkCallbackResult>;
+    ) -> Option<Result<WalkResult>>;
 }
 
 mod sealed {
@@ -783,7 +732,7 @@ where
         &mut self,
         value: &StructuralView,
         _def_region_kind: DefRegionKind,
-    ) -> Option<WalkCallbackResult> {
+    ) -> Option<Result<WalkResult>> {
         value
             .cast::<T>()
             .map(|typed| self(typed).into_walk_result())
@@ -804,7 +753,7 @@ where
         &mut self,
         value: &StructuralView,
         def_region_kind: DefRegionKind,
-    ) -> Option<WalkCallbackResult> {
+    ) -> Option<Result<WalkResult>> {
         value
             .cast::<T>()
             .map(|typed| self(typed, def_region_kind).into_walk_result())
@@ -825,7 +774,7 @@ where
         &mut self,
         value: &StructuralView,
         _def_region_kind: DefRegionKind,
-    ) -> Option<WalkCallbackResult> {
+    ) -> Option<Result<WalkResult>> {
         value
             .as_node::<N>()
             .map(|node| self(node).into_walk_result())
@@ -846,7 +795,7 @@ where
         &mut self,
         value: &StructuralView,
         def_region_kind: DefRegionKind,
-    ) -> Option<WalkCallbackResult> {
+    ) -> Option<Result<WalkResult>> {
         value
             .as_node::<N>()
             .map(|node| self(node, def_region_kind).into_walk_result())
@@ -866,7 +815,7 @@ where
         &mut self,
         value: &StructuralView,
         _def_region_kind: DefRegionKind,
-    ) -> Option<WalkCallbackResult> {
+    ) -> Option<Result<WalkResult>> {
         Some(self(value).into_walk_result())
     }
 }
@@ -884,7 +833,7 @@ where
         &mut self,
         value: &StructuralView,
         def_region_kind: DefRegionKind,
-    ) -> Option<WalkCallbackResult> {
+    ) -> Option<Result<WalkResult>> {
         Some(self(value, def_region_kind).into_walk_result())
     }
 }
@@ -901,7 +850,7 @@ impl<V: WalkDispatch> WalkChainLink<ByWalkDispatchLink> for 
&mut V {
         &mut self,
         value: &StructuralView,
         def_region_kind: DefRegionKind,
-    ) -> Option<WalkCallbackResult> {
+    ) -> Option<Result<WalkResult>> {
         self.dispatch_walk(value, def_region_kind)
     }
 }
@@ -956,7 +905,7 @@ macro_rules! impl_chain_link {
                 &mut self,
                 value: &StructuralView,
                 def_region_kind: DefRegionKind,
-            ) -> Option<WalkCallbackResult> {
+            ) -> Option<Result<WalkResult>> {
                 $(
                     if let Some(result) = self.$idx.try_call(value, 
def_region_kind) {
                         return Some(result);
@@ -1176,6 +1125,18 @@ where
 pub trait NativeVisit: Sized {
     const CUSTOM_DESCENT: bool = false;
 
+    fn walk_root(&mut self, root: AnyView<'_>, order: WalkOrder) -> 
Result<Option<VisitInterrupt>> {
+        let root = raw_of(root);
+        finish(match order {
+            WalkOrder::PreOrder => {
+                run_structural_visitor(root, self, walk_runtime_vtable::<Self, 
true>())
+            }
+            WalkOrder::PostOrder => {
+                run_structural_visitor(root, self, walk_runtime_vtable::<Self, 
false>())
+            }
+        })
+    }
+
     fn visit(
         &mut self,
         value: &StructuralView,
@@ -1191,6 +1152,32 @@ pub trait NativeVisit: Sized {
     }
 }
 
+impl<V: NativeVisit> NativeVisit for &mut V {
+    const CUSTOM_DESCENT: bool = V::CUSTOM_DESCENT;
+
+    fn walk_root(&mut self, root: AnyView<'_>, order: WalkOrder) -> 
Result<Option<VisitInterrupt>> {
+        // Register the actual visitor as the active context, not this 
reference's
+        // stack slot: policy continuations validate that identity when 
reentering.
+        (**self).walk_root(root, order)
+    }
+
+    fn visit(
+        &mut self,
+        value: &StructuralView,
+        def_region_kind: DefRegionKind,
+    ) -> Result<WalkResult> {
+        (**self).visit(value, def_region_kind)
+    }
+
+    fn default_visit_children<const PRE_ORDER: bool>(
+        &mut self,
+        value: &StructuralView,
+        def_region_kind: DefRegionKind,
+    ) -> Result<Option<VisitInterrupt>> {
+        (**self).default_visit_children::<PRE_ORDER>(value, def_region_kind)
+    }
+}
+
 fn default_walk_children<V: NativeVisit, const PRE_ORDER: bool>(
     visitor: &mut V,
     value: &StructuralView,
@@ -1957,7 +1944,8 @@ where
 /// in any [`WalkChainLink`] shape (catch-all `&StructuralView`, typed, or 
node,
 /// with an optional trailing [`DefRegionKind`]), or a tuple of such
 /// callbacks tried in order — the C++ callback overloads and variadic
-/// chain. The walker owns recursion: the handler runs once per value,
+/// chain. A [`WalkWithContextPolicy`] may be passed by value or mutable 
reference.
+/// The walker owns recursion: the handler runs once per value,
 /// before or after the value's children according to `order`, and steers
 /// traversal through the returned [`WalkResult`].
 pub fn structural_walk<R, M, H>(
@@ -1969,21 +1957,7 @@ where
     H: IntoWalker<M>,
     for<'x> AnyView<'x>: From<&'x R>,
 {
-    let mut dispatch = walker.into_walker();
-    let root = raw_of(AnyView::from(root));
-    let result = match order {
-        WalkOrder::PreOrder => run_structural_visitor(
-            root,
-            &mut dispatch,
-            walk_runtime_vtable::<H::Walker, true>(),
-        ),
-        WalkOrder::PostOrder => run_structural_visitor(
-            root,
-            &mut dispatch,
-            walk_runtime_vtable::<H::Walker, false>(),
-        ),
-    };
-    finish(result)
+    walker.into_walker().walk_root(AnyView::from(root), order)
 }
 
 fn finish(result: NativeResult) -> Result<Option<VisitInterrupt>> {
diff --git a/rust/tvm-ffi/src/extra/structural_visit/policy.rs 
b/rust/tvm-ffi/src/extra/structural_visit/policy.rs
index fed91afc..8b8e3d4c 100644
--- a/rust/tvm-ffi/src/extra/structural_visit/policy.rs
+++ b/rust/tvm-ffi/src/extra/structural_visit/policy.rs
@@ -154,7 +154,7 @@ impl<State, V: StructuralVisitor + 
VisitCallbackState<State>> VisitContextDriver
 ///
 /// The dispatcher is also the state visible through the policy's context. Use
 /// `#[dispatch(walk)]` or implement [`WalkDispatch`] to define its callbacks.
-/// Run repeatedly with [`Self::walk`], or pass this value to 
[`structural_walk`].
+/// Pass this value or a mutable reference to [`structural_walk`].
 pub struct WalkWithContextPolicy<Walker, Policy> {
     walker: Walker,
     policy: Rc<Policy>,
@@ -183,22 +183,6 @@ impl<Walker: WalkDispatch, Policy: ContextPolicy<Walker>> 
WalkWithContextPolicy<
     pub fn into_state(self) -> Walker {
         self.walker
     }
-
-    /// Walk a root using this dispatcher and policy.
-    pub fn walk<R>(&mut self, root: &R, order: WalkOrder) -> 
Result<Option<VisitInterrupt>>
-    where
-        for<'x> AnyView<'x>: From<&'x R>,
-    {
-        let raw = raw_of(AnyView::from(root));
-        finish(match order {
-            WalkOrder::PreOrder => {
-                run_structural_visitor(raw, self, walk_runtime_vtable::<Self, 
true>())
-            }
-            WalkOrder::PostOrder => {
-                run_structural_visitor(raw, self, walk_runtime_vtable::<Self, 
false>())
-            }
-        })
-    }
 }
 
 #[doc(hidden)]
@@ -213,6 +197,15 @@ impl<Walker: WalkDispatch, Policy: ContextPolicy<Walker>> 
IntoWalker<ByPolicyWal
     }
 }
 
+impl<Walker: WalkDispatch, Policy: ContextPolicy<Walker>> 
IntoWalker<ByPolicyWalk>
+    for &mut WalkWithContextPolicy<Walker, Policy>
+{
+    type Walker = Self;
+    fn into_walker(self) -> Self {
+        self
+    }
+}
+
 impl<Walker: WalkDispatch, Policy: ContextPolicy<Walker>> NativeVisit
     for WalkWithContextPolicy<Walker, Policy>
 {
diff --git a/rust/tvm-ffi/src/lib.rs b/rust/tvm-ffi/src/lib.rs
index eadc138c..2bc2bb81 100644
--- a/rust/tvm-ffi/src/lib.rs
+++ b/rust/tvm-ffi/src/lib.rs
@@ -49,15 +49,15 @@ pub use crate::error::{
 };
 pub use crate::extra::module::Module;
 pub use crate::extra::structural_mutate::{
-    structural_map, structural_mutate, CallbackMutator, 
DefaultMutContextPolicy, InplaceMode,
-    InplaceValue, IntoMapResult, IntoMapper, IntoMutator, MapChainLink, 
MapDispatch, MapValue,
-    MapWithContextPolicy, MutContextPolicy, MutateCallbacks, MutateChainLink, 
MutateContext,
-    MutateDispatch, MutateValue, Mutator, StructuralMutator, 
StructuralVarRemap,
+    structural_map, structural_mutate, DefaultMutContextPolicy, InplaceMode, 
InplaceValue,
+    IntoMapper, IntoMutator, MapChainLink, MapDispatch, MapWithContextPolicy, 
MutContextPolicy,
+    MutateCallbacks, MutateChainLink, MutateContext, MutateDispatch, 
MutateValue, Mutator,
+    StructuralMutator, StructuralVarRemap,
 };
 pub use crate::extra::structural_visit::{
     structural_visit, structural_walk, ContextPolicy, DefRegionKind, 
DefaultContextPolicy,
     IntoVisitor, IntoWalkResult, IntoWalker, StructuralVisitor, 
VisitCallbacks, VisitChainLink,
-    VisitContext, VisitInterrupt, VisitValue, WalkChainLink, WalkDispatch, 
WalkOrder, WalkResult,
+    VisitContext, VisitInterrupt, WalkChainLink, WalkDispatch, WalkOrder, 
WalkResult,
     WalkWithContextPolicy,
 };
 pub use crate::extra::unchanged::{Unchanged, UnchangedOr};
diff --git a/rust/tvm-ffi/tests/test_structural_mutate.rs 
b/rust/tvm-ffi/tests/test_structural_mutate.rs
index 70183e9c..fe1d0f81 100644
--- a/rust/tvm-ffi/tests/test_structural_mutate.rs
+++ b/rust/tvm-ffi/tests/test_structural_mutate.rs
@@ -22,11 +22,11 @@ use tvm_ffi::collections::map::MapObj;
 use tvm_ffi::function::FunctionObj;
 use tvm_ffi::object::ObjectRef;
 use tvm_ffi::{
-    dispatch, structural_map, structural_mutate, Any, AnyView, Array, 
CallbackMutator,
-    DefRegionKind, DefaultMutContextPolicy, Error, FieldGetter, Function, 
InplaceMode,
-    InplaceValue, IntoMapper, Map, MapDispatch, MapValue, 
MapWithContextPolicy, MutContextPolicy,
-    MutateCallbacks, MutateValue, Mutator, Object, ObjectArc, ObjectRefCore, 
Result,
-    String as FfiString, StructuralMutator, StructuralVarRemap, TypeIndex, 
Unchanged, UnchangedOr,
+    dispatch, structural_map, structural_mutate, Any, AnyView, Array, 
DefRegionKind,
+    DefaultMutContextPolicy, Error, FieldGetter, Function, InplaceMode, 
InplaceValue, IntoMapper,
+    Map, MapDispatch, MapWithContextPolicy, MutContextPolicy, MutateCallbacks, 
MutateContext,
+    MutateValue, Mutator, Object, ObjectArc, ObjectRefCore, Result, String as 
FfiString,
+    StructuralMutator, StructuralVarRemap, StructuralView, TypeIndex, 
Unchanged, UnchangedOr,
     WalkOrder, RUNTIME_ERROR,
 };
 
@@ -35,7 +35,7 @@ struct IncrementIntegers;
 impl MapDispatch for IncrementIntegers {
     fn dispatch_map(
         &mut self,
-        value: &MapValue,
+        value: &StructuralView,
         _def_region_kind: DefRegionKind,
     ) -> Option<Result<Any>> {
         value
@@ -50,7 +50,11 @@ struct ManualIncrement {
 }
 
 impl StructuralMutator for ManualIncrement {
-    fn dispatch_mutate(&mut self, value: &MapValue, def_region_kind: 
DefRegionKind) -> Result<Any> {
+    fn dispatch_mutate(
+        &mut self,
+        value: &StructuralView,
+        def_region_kind: DefRegionKind,
+    ) -> Result<Any> {
         if let Some(integer) = value.cast::<i64>() {
             Ok(Any::from(integer + 1))
         } else {
@@ -66,11 +70,11 @@ impl StructuralMutator for ManualIncrement {
         self.default_maybe_inplace_mutate(value, def_region_kind)
     }
 
-    fn var_remap_get(&mut self, var: &MapValue) -> Result<Option<Any>> {
+    fn var_remap_get(&mut self, var: &StructuralView) -> Result<Option<Any>> {
         self.remap.get(var)
     }
 
-    fn var_remap_set(&mut self, var: &MapValue, mutated_value: &Any) -> 
Result<()> {
+    fn var_remap_set(&mut self, var: &StructuralView, mutated_value: &Any) -> 
Result<()> {
         self.remap.set(var, mutated_value)
     }
 }
@@ -82,7 +86,11 @@ struct ReplaceNone {
 }
 
 impl StructuralMutator for ReplaceNone {
-    fn dispatch_mutate(&mut self, value: &MapValue, def_region_kind: 
DefRegionKind) -> Result<Any> {
+    fn dispatch_mutate(
+        &mut self,
+        value: &StructuralView,
+        def_region_kind: DefRegionKind,
+    ) -> Result<Any> {
         if value.type_index() == TypeIndex::kTVMFFINone as i32 {
             self.calls += 1;
             Ok(Any::from(8i64))
@@ -99,11 +107,11 @@ impl StructuralMutator for ReplaceNone {
         self.default_maybe_inplace_mutate(value, def_region_kind)
     }
 
-    fn var_remap_get(&mut self, var: &MapValue) -> Result<Option<Any>> {
+    fn var_remap_get(&mut self, var: &StructuralView) -> Result<Option<Any>> {
         self.remap.get(var)
     }
 
-    fn var_remap_set(&mut self, var: &MapValue, mutated_value: &Any) -> 
Result<()> {
+    fn var_remap_set(&mut self, var: &StructuralView, mutated_value: &Any) -> 
Result<()> {
         self.remap.set(var, mutated_value)
     }
 }
@@ -115,7 +123,11 @@ struct RecursiveEntryMutator {
 }
 
 impl StructuralMutator for RecursiveEntryMutator {
-    fn dispatch_mutate(&mut self, value: &MapValue, def_region_kind: 
DefRegionKind) -> Result<Any> {
+    fn dispatch_mutate(
+        &mut self,
+        value: &StructuralView,
+        def_region_kind: DefRegionKind,
+    ) -> Result<Any> {
         if value.type_index() == TypeIndex::kTVMFFINone as i32 {
             if self.use_owned_value {
                 let value = Array::new(vec![1i64]);
@@ -139,11 +151,11 @@ impl StructuralMutator for RecursiveEntryMutator {
         self.default_maybe_inplace_mutate(value, def_region_kind)
     }
 
-    fn var_remap_get(&mut self, var: &MapValue) -> Result<Option<Any>> {
+    fn var_remap_get(&mut self, var: &StructuralView) -> Result<Option<Any>> {
         self.remap.get(var)
     }
 
-    fn var_remap_set(&mut self, var: &MapValue, mutated_value: &Any) -> 
Result<()> {
+    fn var_remap_set(&mut self, var: &StructuralView, mutated_value: &Any) -> 
Result<()> {
         self.remap.set(var, mutated_value)
     }
 }
@@ -283,7 +295,7 @@ fn 
none_values_are_dispatched_to_map_callbacks_and_user_mutators() {
     let mut map_calls = 0;
     let mapped = structural_map(
         Any::new(),
-        |value: &MapValue| {
+        |value: &StructuralView| {
             map_calls += 1;
             assert_eq!(value.type_index(), TypeIndex::kTVMFFINone as i32);
             Any::from(7i64)
@@ -337,7 +349,7 @@ fn 
default_mutation_mode_preserves_ownership_and_unchanged_results() {
         increment: bool,
     }
     impl StructuralMutator for Controlled {
-        fn dispatch_mutate(&mut self, value: &MapValue, _: DefRegionKind) -> 
Result<Any> {
+        fn dispatch_mutate(&mut self, value: &StructuralView, _: 
DefRegionKind) -> Result<Any> {
             let integer = value.cast::<i64>().unwrap();
             Ok(if self.increment {
                 Any::from(integer + 1)
@@ -410,12 +422,12 @@ fn 
owned_entry_mode_is_forwarded_by_generated_and_closure_callbacks() {
         let result = structural_mutate(
             true,
             (
-                |_: bool, ctx: &mut CallbackMutator| {
+                |_: bool, ctx: &mut MutateContext<'_>| {
                     let child = Array::new(vec![1_i64]);
                     pointer.set(array_pointer(&child) as usize);
                     ctx.maybe_inplace_mutate_with_mode(child, 
DefRegionKind::Pattern, mode)
                 },
-                |value: i64, ctx: &mut CallbackMutator| {
+                |value: i64, ctx: &mut MutateContext<'_>| {
                     assert_eq!(ctx.def_region_kind(), DefRegionKind::Pattern);
                     value + 1
                 },
@@ -463,16 +475,13 @@ fn 
consuming_callbacks_forward_permissions_without_temporary_owners() {
             &mut self,
             value: MutateValue<'_, Array<Any>>,
             ctx: &mut Mutator,
-            mode: InplaceMode,
         ) -> Result<UnchangedOr<Any>> {
-            assert_eq!(mode, ctx.inplace_mode());
-            assert_eq!(mode, value.inplace_mode());
+            assert_eq!(ctx.inplace_mode(), value.inplace_mode());
             self.observe(&value);
             ctx.default_mutate_with_mode_result(self, value, self.requested)
         }
-        fn mutate_integer(&mut self, value: i64, ctx: &mut Mutator, mode: 
InplaceMode) -> i64 {
-            assert_eq!(mode, InplaceMode::Disallow);
-            assert_eq!(mode, ctx.inplace_mode());
+        fn mutate_integer(&mut self, value: i64, ctx: &mut Mutator) -> i64 {
+            assert_eq!(ctx.inplace_mode(), InplaceMode::Disallow);
             value + 1
         }
     }
@@ -499,11 +508,11 @@ fn 
consuming_callbacks_forward_permissions_without_temporary_owners() {
                 structural_mutate(root, &mut state).unwrap()
             } else {
                 let miss = |_: MutateValue<'_, FfiString>,
-                            _: &mut CallbackMutator<Forward>|
+                            _: &mut MutateContext<'_, Forward>|
                  -> Any { panic!("typed miss must continue") };
-                let increment = |value: i64, _: &mut CallbackMutator<Forward>| 
value + 1;
+                let increment = |value: i64, _: &mut MutateContext<'_, 
Forward>| value + 1;
                 let descend = |value: MutateValue<'_, Array<Any>>,
-                               ctx: &mut CallbackMutator<Forward>| {
+                               ctx: &mut MutateContext<'_, Forward>| {
                     assert_eq!(value.inplace_mode(), ctx.inplace_mode());
                     ctx.state_mut().observe(&value);
                     let requested = ctx.state().requested;
@@ -579,7 +588,7 @@ fn 
consuming_default_descent_transfers_between_contexts_without_node_borrows() {
             let mut callbacks = MutateCallbacks::new(
                 inner,
                 (
-                    |_: bool, ctx: &mut CallbackMutator<Inner<'_>>| -> 
Result<Any> {
+                    |_: bool, ctx: &mut MutateContext<'_, Inner<'_>>| -> 
Result<Any> {
                         let value = ctx.state_mut().value.take().unwrap();
                         let result = if ctx.state().preserve_unchanged {
                             
ctx.default_maybe_inplace_mutate_result(value)?.into()
@@ -589,7 +598,7 @@ fn 
consuming_default_descent_transfers_between_contexts_without_node_borrows() {
                         ctx.state_mut().result = result;
                         Ok(Any::new())
                     },
-                    |value: i64, _: &mut CallbackMutator<Inner<'_>>| value + 1,
+                    |value: i64, _: &mut MutateContext<'_, Inner<'_>>| value + 
1,
                 ),
             );
             structural_mutate(true, &mut callbacks)?;
@@ -618,7 +627,7 @@ fn 
consuming_default_descent_transfers_between_contexts_without_node_borrows() {
                 let result = if generated {
                     structural_mutate(root, &mut Outer { preserve, retain })
                 } else {
-                    structural_mutate(root, |value: MutateValue<'_>, _: &mut 
CallbackMutator| {
+                    structural_mutate(root, |value: MutateValue<'_>, _: &mut 
MutateContext<'_>| {
                         transfer(value, false, preserve, retain)
                     })
                 }
@@ -638,7 +647,7 @@ fn 
consuming_default_descent_preserves_unchanged_and_propagates_errors() {
         let pointer = array_pointer(&root);
         let result = structural_mutate(
             root,
-            |value: MutateValue<'_>, ctx: &mut CallbackMutator| -> 
Result<UnchangedOr<Any>> {
+            |value: MutateValue<'_>, ctx: &mut MutateContext<'_>| -> 
Result<UnchangedOr<Any>> {
                 if value.cast::<i64>().is_some() {
                     return if fail {
                         Err(Error::new(RUNTIME_ERROR, "child failed", ""))
@@ -751,7 +760,7 @@ fn 
callback_errors_preserve_message_and_add_object_context() {
 
     let error = match structural_mutate(
         Array::new(vec![1i64]),
-        |_integer: i64, _mutator: &mut CallbackMutator| -> Result<i64> {
+        |_integer: i64, _mutator: &mut MutateContext<'_>| -> Result<i64> {
             Err(Error::new(
                 RUNTIME_ERROR,
                 "callback mutator failed",
@@ -943,7 +952,7 @@ struct GeneratedLeafDispatch {
 #[dispatch(mutate)]
 impl GeneratedLeafDispatch {
     fn mutate_integer(&mut self, value: i64, mutator: &mut Mutator) -> Any {
-        let region = mutator.region();
+        let region = mutator.def_region_kind();
         self.integers.push((value, region));
         Any::from(value + 1)
     }
@@ -994,7 +1003,7 @@ struct GeneratedRecursiveDispatch {
 #[dispatch(mutate)]
 impl GeneratedRecursiveDispatch {
     fn mutate_array(&mut self, array: Array<i64>, mutator: &mut Mutator) -> 
Result<Array<i64>> {
-        let region = mutator.region();
+        let region = mutator.def_region_kind();
         self.arrays.push(region);
         let mut mutated = Vec::with_capacity(array.len());
         for value in array.iter() {
@@ -1004,7 +1013,7 @@ impl GeneratedRecursiveDispatch {
     }
 
     fn mutate_integer(&mut self, value: i64, mutator: &mut Mutator) -> Any {
-        let region = mutator.region();
+        let region = mutator.def_region_kind();
         self.integers.push((value, region));
         Any::from(value + 10)
     }
@@ -1073,7 +1082,7 @@ fn pre_order_retained_alias_disables_in_place_mutation() {
     let mut retained = None;
     let mapped = structural_map(
         root,
-        |value: &MapValue| {
+        |value: &StructuralView| {
             if value.type_index() == TypeIndex::kTVMFFIList as i32 {
                 retained = Some(value.to_owned());
                 value.to_owned()
@@ -1151,7 +1160,7 @@ fn callbacks_return_values_convertible_into_any() {
 
     let mutated = structural_mutate(
         Array::new(vec![1i64, 2]),
-        |integer: i64, _mutator: &mut CallbackMutator| integer * 2,
+        |integer: i64, _mutator: &mut MutateContext<'_>| integer * 2,
     )
     .and_then(Array::<i64>::try_from)
     .unwrap();
@@ -1161,8 +1170,8 @@ fn callbacks_return_values_convertible_into_any() {
 #[test]
 fn recursive_mutate_returns_unchanged_or_a_replacement() {
     fn clamp_negative_integers(
-        value: &MapValue,
-        mutator: &mut CallbackMutator,
+        value: &StructuralView,
+        mutator: &mut MutateContext<'_>,
     ) -> Result<UnchangedOr<Any>> {
         if let Some(integer) = value.cast::<i64>() {
             if integer >= 0 {
@@ -1211,7 +1220,7 @@ fn pre_order_unchanged_reuses_unmodified_subtrees() {
                 }
             },
             // Keeping an array still lets pre-order map transform its 
children.
-            |_value: &MapValue| Unchanged,
+            |_value: &StructuralView| Unchanged,
         ),
         WalkOrder::PreOrder,
     )
@@ -1233,6 +1242,42 @@ fn pre_order_unchanged_reuses_unmodified_subtrees() {
     );
 }
 
+#[test]
+fn post_order_unchanged_preserves_descendant_rewrites() {
+    for with_policy in [false, true] {
+        for shared in [false, true] {
+            let root = Array::new(vec![1_i64]);
+            let pointer = array_pointer(&root);
+            let alias = shared.then(|| root.clone());
+            let callbacks = (
+                |integer: i64| integer + 1,
+                |value: &StructuralView| {
+                    
assert_eq!(value.cast::<Array<i64>>().unwrap().get(0).unwrap(), 2);
+                    Unchanged
+                },
+            );
+            let mapped = if with_policy {
+                structural_map(
+                    root,
+                    MapWithContextPolicy::new(callbacks.into_mapper(), 
DefaultMutContextPolicy),
+                    WalkOrder::PostOrder,
+                )
+            } else {
+                structural_map(root, callbacks, WalkOrder::PostOrder)
+            }
+            .and_then(Array::<i64>::try_from)
+            .unwrap();
+            assert_eq!(mapped.get(0).unwrap(), 2);
+            if let Some(alias) = alias {
+                assert_eq!(alias.get(0).unwrap(), 1);
+                assert_ne!(array_pointer(&mapped), pointer);
+            } else {
+                assert_eq!(array_pointer(&mapped), pointer);
+            }
+        }
+    }
+}
+
 #[test]
 fn twelve_link_tuple_reaches_final_map_dispatch() {
     let mut final_dispatch = IncrementIntegers;
@@ -1285,7 +1330,7 @@ fn nested_tuple_chain_exceeds_flat_arity() {
                 |value: i64| Any::from(value * 10),
                 (
                     |value: Array<FfiString>| Any::from(value),
-                    (|value: &MapValue| {
+                    (|value: &StructuralView| {
                         catch_all += 1;
                         value.to_owned()
                     },),
@@ -1306,7 +1351,7 @@ fn callbacks_run_in_the_configured_order() {
     let mut pre = Vec::new();
     structural_map(
         root.clone(),
-        |value: &MapValue| {
+        |value: &StructuralView| {
             pre.push(value.cast::<i64>());
             value.to_owned()
         },
@@ -1318,7 +1363,7 @@ fn callbacks_run_in_the_configured_order() {
     let mut post = Vec::new();
     structural_map(
         root,
-        |value: &MapValue| {
+        |value: &StructuralView| {
             post.push(value.cast::<i64>());
             value.to_owned()
         },
@@ -1381,7 +1426,7 @@ fn map_keys_are_anchors_and_object_leaves_are_preserved() 
{
 fn callback_mutate_defaults_unmatched_values_and_preserves_root_permit() {
     let root = Array::new(vec![1i64, 2]);
     let root_pointer = array_pointer(&root);
-    let mutated = structural_mutate(root, |value: i64, _mutator: &mut 
CallbackMutator| {
+    let mutated = structural_mutate(root, |value: i64, _mutator: &mut 
MutateContext<'_>| {
         Any::from(value + 1)
     })
     .and_then(Array::<i64>::try_from)
@@ -1396,14 +1441,17 @@ struct CallbackMutateStats {
     defaults: usize,
 }
 
-fn stateful_mutate_integer(value: i64, mutator: &mut 
CallbackMutator<CallbackMutateStats>) -> Any {
+fn stateful_mutate_integer(
+    value: i64,
+    mutator: &mut MutateContext<'_, CallbackMutateStats>,
+) -> Any {
     mutator.state_mut().integers.push(value);
     Any::from(value + 1)
 }
 
 fn stateful_mutate_default(
     value: &tvm_ffi::StructuralView,
-    mutator: &mut CallbackMutator<CallbackMutateStats>,
+    mutator: &mut MutateContext<'_, CallbackMutateStats>,
 ) -> Result<Any> {
     mutator.state_mut().defaults += 1;
     mutator.default_mutate(value)
@@ -1441,8 +1489,8 @@ struct CallbackMutateDepth {
 }
 
 fn stateful_mutate_recursive(
-    value: &MapValue,
-    mutator: &mut CallbackMutator<CallbackMutateDepth>,
+    value: &StructuralView,
+    mutator: &mut MutateContext<'_, CallbackMutateDepth>,
 ) -> Result<Any> {
     {
         let state = mutator.state_mut();
@@ -1484,8 +1532,8 @@ fn 
callback_mutate_explicit_default_is_repeatable_copy_path() {
     let mutated = structural_mutate(
         root,
         (
-            |value: i64, _mutator: &mut CallbackMutator| Any::from(value + 1),
-            |value: &MapValue, mutator: &mut CallbackMutator| -> Result<Any> {
+            |value: i64, _mutator: &mut MutateContext<'_>| Any::from(value + 
1),
+            |value: &StructuralView, mutator: &mut MutateContext<'_>| -> 
Result<Any> {
                 defaults.set(defaults.get() + 1);
                 let first = mutator.default_mutate(value)?;
                 let second = mutator.default_mutate(value)?;
@@ -1507,8 +1555,10 @@ fn 
callback_mutate_match_is_final_and_same_fn_can_reenter() {
     let mutated = structural_mutate(
         Array::new(vec![1i64]),
         (
-            |_array: Array<i64>, _mutator: &mut CallbackMutator| 
Any::from(Array::new(vec![10i64])),
-            |value: i64, _mutator: &mut CallbackMutator| {
+            |_array: Array<i64>, _mutator: &mut MutateContext<'_>| {
+                Any::from(Array::new(vec![10i64]))
+            },
+            |value: i64, _mutator: &mut MutateContext<'_>| {
                 integer_calls.set(integer_calls.get() + 1);
                 Any::from(value + 1)
             },
@@ -1522,7 +1572,7 @@ fn 
callback_mutate_match_is_final_and_same_fn_can_reenter() {
     let calls = Cell::new(0);
     let mutated = structural_mutate(
         Array::new(vec![1i64, 2]),
-        |value: &MapValue, mutator: &mut CallbackMutator| {
+        |value: &StructuralView, mutator: &mut MutateContext<'_>| {
             calls.set(calls.get() + 1);
             mutator.default_mutate(value)
         },
@@ -1547,10 +1597,10 @@ fn 
callback_mutate_supports_node_links_nested_tuples_and_reflection() {
         root,
         (
             (
-                |_value: bool, _mutator: &mut CallbackMutator| Any::new(),
-                |_node: &FunctionObj, _mutator: &mut CallbackMutator| 
Any::from(7i64),
+                |_value: bool, _mutator: &mut MutateContext<'_>| Any::new(),
+                |_node: &FunctionObj, _mutator: &mut MutateContext<'_>| 
Any::from(7i64),
             ),
-            |value: i64, mutator: &mut CallbackMutator| {
+            |value: i64, mutator: &mut MutateContext<'_>| {
                 regions.borrow_mut().push(mutator.def_region_kind());
                 Any::from(value + 1)
             },
@@ -1576,8 +1626,8 @@ fn 
callback_mutate_distinguishes_borrowed_and_owned_children() {
     let mutated = structural_mutate(
         true,
         (
-            |_value: bool, mutator: &mut CallbackMutator| 
mutator.mutate(&borrowed_child),
-            |value: i64, _mutator: &mut CallbackMutator| Any::from(value + 1),
+            |_value: bool, mutator: &mut MutateContext<'_>| 
mutator.mutate(&borrowed_child),
+            |value: i64, _mutator: &mut MutateContext<'_>| Any::from(value + 
1),
         ),
     )
     .and_then(Array::<i64>::try_from)
@@ -1590,12 +1640,12 @@ fn 
callback_mutate_distinguishes_borrowed_and_owned_children() {
     let mutated = structural_mutate(
         true,
         (
-            |_value: bool, mutator: &mut CallbackMutator| {
+            |_value: bool, mutator: &mut MutateContext<'_>| {
                 let child = Array::new(vec![1i64]);
                 owned_pointer.set(array_pointer(&child) as usize);
                 mutator.maybe_inplace_mutate(child)
             },
-            |value: i64, _mutator: &mut CallbackMutator| Any::from(value + 1),
+            |value: i64, _mutator: &mut MutateContext<'_>| Any::from(value + 
1),
         ),
     )
     .and_then(Array::<i64>::try_from)
@@ -1608,11 +1658,11 @@ fn 
callback_mutate_distinguishes_borrowed_and_owned_children() {
 fn nested_callback_mutate_restores_the_outer_active_mutator() {
     let mutated = structural_mutate(
         1i64,
-        |value: i64, mutator: &mut CallbackMutator| -> Result<Any> {
+        |value: i64, mutator: &mut MutateContext<'_>| -> Result<Any> {
             if value != 1 {
                 return Ok(Any::from(value + 1));
             }
-            let inner = structural_mutate(2i64, |value: i64, _mutator: &mut 
CallbackMutator| {
+            let inner = structural_mutate(2i64, |value: i64, _mutator: &mut 
MutateContext<'_>| {
                 Any::from(value + 10)
             })?;
             assert_eq!(i64::try_from(inner).unwrap(), 12);
@@ -1629,7 +1679,7 @@ fn 
callback_mutate_panics_resume_and_leave_the_next_run_usable() {
     let panic = match std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| 
{
         structural_mutate(
             Array::new(vec![1i64]),
-            |_value: i64, _mutator: &mut CallbackMutator| -> Any {
+            |_value: i64, _mutator: &mut MutateContext<'_>| -> Any {
                 panic!("callback mutator panic")
             },
         )
@@ -1644,7 +1694,7 @@ fn 
callback_mutate_panics_resume_and_leave_the_next_run_usable() {
 
     let mutated = structural_mutate(
         Array::new(vec![1i64]),
-        |value: i64, _mutator: &mut CallbackMutator| Any::from(value + 1),
+        |value: i64, _mutator: &mut MutateContext<'_>| Any::from(value + 1),
     )
     .and_then(Array::<i64>::try_from)
     .unwrap();
@@ -1673,7 +1723,7 @@ impl MutContextPolicy<PolicyState> for ArrayPolicy {
     fn default_mutate(
         &self,
         value: MutateValue<'_>,
-        ctx: &mut CallbackMutator<PolicyState>,
+        ctx: &mut MutateContext<'_, PolicyState>,
     ) -> Result<UnchangedOr<Any>> {
         if value
             .as_node::<tvm_ffi::collections::array::ArrayObj>()
@@ -1696,7 +1746,7 @@ impl MutContextPolicy<PolicyState> for RecordPolicy {
     fn default_mutate(
         &self,
         value: MutateValue<'_>,
-        ctx: &mut CallbackMutator<PolicyState>,
+        ctx: &mut MutateContext<'_, PolicyState>,
     ) -> Result<UnchangedOr<Any>> {
         if value
             .as_node::<tvm_ffi::collections::array::ArrayObj>()
@@ -1760,12 +1810,12 @@ fn 
mutation_policies_share_state_and_preserve_callback_order() {
     let mut mutator = MutateCallbacks::new(
         PolicyState::default(),
         (
-            |x: i64, ctx: &mut CallbackMutator<PolicyState>| {
+            |x: i64, ctx: &mut MutateContext<'_, PolicyState>| {
                 let depth = ctx.state().depth;
                 ctx.state_mut().events.push(("integer", depth));
                 x + 1
             },
-            |value: MutateValue<'_>, ctx: &mut CallbackMutator<PolicyState>| {
+            |value: MutateValue<'_>, ctx: &mut MutateContext<'_, PolicyState>| 
{
                 let depth = ctx.state().depth;
                 ctx.state_mut().events.push(("callback", depth));
                 ctx.default_maybe_inplace_mutate_result(value)
@@ -1786,7 +1836,7 @@ fn 
map_policy_entries_preserve_descent_and_callback_composition() {
         fn default_mutate(
             &self,
             _: MutateValue<'_>,
-            _: &mut CallbackMutator<State>,
+            _: &mut MutateContext<'_, State>,
         ) -> Result<UnchangedOr<Any>> {
             Ok(UnchangedOr::unchanged())
         }
@@ -1795,15 +1845,14 @@ fn 
map_policy_entries_preserve_descent_and_callback_composition() {
     let root = || Array::new(vec![1_i64]);
     let first = |value: Any| 
Array::<i64>::try_from(value).unwrap().get(0).unwrap();
     for order in [WalkOrder::PreOrder, WalkOrder::PostOrder] {
-        for entry in 0..3 {
+        for entry in 0..2 {
             let mut state = PolicyState::default();
             let output = {
                 let mut mapper =
                     MapWithContextPolicy::new(&mut state, 
(DefaultMutContextPolicy, Stop));
                 match entry {
                     0 => structural_map(root(), mapper, order),
-                    1 => structural_map(root(), &mut mapper, order),
-                    _ => mapper.map(root(), order),
+                    _ => structural_map(root(), &mut mapper, order),
                 }
             }
             .unwrap();
@@ -1846,7 +1895,7 @@ fn 
mutation_policy_continuations_preserve_ownership_and_markers() {
         fn default_mutate(
             &self,
             value: MutateValue<'_>,
-            ctx: &mut CallbackMutator<Ownership>,
+            ctx: &mut MutateContext<'_, Ownership>,
         ) -> Result<UnchangedOr<Any>> {
             let array = value
                 .as_node::<tvm_ffi::collections::array::ArrayObj>()
@@ -1886,25 +1935,25 @@ fn 
mutation_policy_continuations_preserve_ownership_and_markers() {
                 };
                 let (output, state) = if entry < 2 {
                     let mut mapper = MapWithContextPolicy::new(state, policy);
-                    let output = mapper
-                        .map(
-                            root,
-                            if entry == 0 {
-                                WalkOrder::PreOrder
-                            } else {
-                                WalkOrder::PostOrder
-                            },
-                        )
-                        .unwrap();
+                    let output = structural_map(
+                        root,
+                        &mut mapper,
+                        if entry == 0 {
+                            WalkOrder::PreOrder
+                        } else {
+                            WalkOrder::PostOrder
+                        },
+                    )
+                    .unwrap();
                     (output, mapper.into_state())
                 } else {
                     let mut mutator = MutateCallbacks::new(
                         state,
                         (
-                            |x: i64, ctx: &mut CallbackMutator<Ownership>| {
+                            |x: i64, ctx: &mut MutateContext<'_, Ownership>| {
                                 ctx.state_mut().map_integer(x)
                             },
-                            |value: MutateValue<'_>, ctx: &mut 
CallbackMutator<Ownership>| {
+                            |value: MutateValue<'_>, ctx: &mut 
MutateContext<'_, Ownership>| {
                                 ctx.default_maybe_inplace_mutate_result(value)
                             },
                         ),
@@ -1948,7 +1997,7 @@ fn 
mutation_policy_regions_retargeting_and_error_restore() {
         fn default_mutate(
             &self,
             value: MutateValue<'_>,
-            ctx: &mut CallbackMutator<Regions>,
+            ctx: &mut MutateContext<'_, Regions>,
         ) -> Result<UnchangedOr<Any>> {
             if value.cast::<bool>() == Some(false) {
                 // Bypass this container's callback, but enter the next policy 
and redispatch its children.
@@ -1975,7 +2024,7 @@ fn 
mutation_policy_regions_retargeting_and_error_restore() {
         fn default_mutate(
             &self,
             value: MutateValue<'_>,
-            ctx: &mut CallbackMutator<Regions>,
+            ctx: &mut MutateContext<'_, Regions>,
         ) -> Result<UnchangedOr<Any>> {
             if value
                 .as_node::<tvm_ffi::collections::array::ArrayObj>()
@@ -1996,7 +2045,7 @@ fn 
mutation_policy_regions_retargeting_and_error_restore() {
         for order in [WalkOrder::PreOrder, WalkOrder::PostOrder] {
             let mut mapper =
                 MapWithContextPolicy::new(Regions::default(), 
(Redirect(requested), Observe));
-            let output = mapper.map(false, order).unwrap();
+            let output = structural_map(false, &mut mapper, order).unwrap();
             assert_eq!(i64::try_from(array_item(&output, 0)).unwrap(), 1);
             let mut expected = vec![(100, Pattern), (1, Pattern)];
             if order == WalkOrder::PostOrder {
@@ -2008,7 +2057,7 @@ fn 
mutation_policy_regions_retargeting_and_error_restore() {
                 .unwrap()
                 .call_tuple((false,))
                 .unwrap();
-            mapper.map(graph, order).unwrap();
+            structural_map(graph, &mut mapper, order).unwrap();
             let expected = if order == WalkOrder::PreOrder {
                 vec![(1, Simple), (2, Pattern), (99, Pattern), (3, Use)]
             } else {
@@ -2018,7 +2067,7 @@ fn 
mutation_policy_regions_retargeting_and_error_restore() {
         }
         let mut mutator = MutateCallbacks::new(
             Regions::default(),
-            |value: MutateValue<'_>, ctx: &mut CallbackMutator<Regions>| {
+            |value: MutateValue<'_>, ctx: &mut MutateContext<'_, Regions>| {
                 if let Some(x) = value.cast::<i64>() {
                     let kind = ctx.def_region_kind();
                     ctx.state_mut().0.push((x, kind));
@@ -2057,7 +2106,7 @@ fn 
mutation_policy_halts_restore_state_and_skip_later_policies() {
         fn default_mutate(
             &self,
             _: MutateValue<'_>,
-            _: &mut CallbackMutator<PolicyState>,
+            _: &mut MutateContext<'_, PolicyState>,
         ) -> Result<UnchangedOr<Any>> {
             if self.0 {
                 Err(Error::new(RUNTIME_ERROR, "stop descent", ""))
@@ -2072,7 +2121,7 @@ fn 
mutation_policy_halts_restore_state_and_skip_later_policies() {
                 PolicyState::default(),
                 (ArrayPolicy, (Halt(fail), RecordPolicy)),
             );
-            let result = mapper.map(Array::new(vec![1_i64]), order);
+            let result = structural_map(Array::new(vec![1_i64]), &mut mapper, 
order);
             assert_eq!(result.is_err(), fail);
             assert_eq!(mapper.state().depth, 0);
             assert!(!mapper
@@ -2086,7 +2135,7 @@ fn 
mutation_policy_halts_restore_state_and_skip_later_policies() {
         }
         let mut mutator = MutateCallbacks::new(
             PolicyState::default(),
-            |x: i64, _: &mut CallbackMutator<PolicyState>| x + 1,
+            |x: i64, _: &mut MutateContext<'_, PolicyState>| x + 1,
         )
         .with_policy((ArrayPolicy, (Halt(fail), RecordPolicy)));
         assert_eq!(
diff --git a/rust/tvm-ffi/tests/test_structural_visit.rs 
b/rust/tvm-ffi/tests/test_structural_visit.rs
index 22137fa6..4901b305 100644
--- a/rust/tvm-ffi/tests/test_structural_visit.rs
+++ b/rust/tvm-ffi/tests/test_structural_visit.rs
@@ -22,8 +22,8 @@ use tvm_ffi::object::ObjectRef;
 use tvm_ffi::{
     dispatch, get_type_attr, structural_visit, structural_walk, Any, Array, 
ContextPolicy,
     DLDataType, DLDataTypeCode, DefRegionKind, Error, FieldGetter, Function, 
Map, Object,
-    ObjectRefCore, Result, String as FfiString, StructuralVisitor, TypeIndex, 
VisitCallbacks,
-    VisitContext, VisitInterrupt, VisitValue, WalkOrder, WalkResult, 
WalkWithContextPolicy,
+    ObjectRefCore, Result, String as FfiString, StructuralView, 
StructuralVisitor, TypeIndex,
+    VisitCallbacks, VisitContext, VisitInterrupt, WalkOrder, WalkResult, 
WalkWithContextPolicy,
     RUNTIME_ERROR,
 };
 
@@ -53,7 +53,7 @@ fn 
composed_policies_share_array_scope_with_visit_and_walk_callbacks() {
     impl ContextPolicy<CollectIntegers> for ArrayScope {
         fn default_visit(
             &self,
-            value: &VisitValue,
+            value: &StructuralView,
             visitor: &mut VisitContext<'_, CollectIntegers>,
         ) -> Result<Option<VisitInterrupt>> {
             if value.cast::<Array<Any>>().is_none() {
@@ -78,7 +78,7 @@ fn 
composed_policies_share_array_scope_with_visit_and_walk_callbacks() {
     impl ContextPolicy<CollectIntegers> for RecordDescent {
         fn default_visit(
             &self,
-            _value: &VisitValue,
+            _value: &StructuralView,
             visitor: &mut VisitContext<'_, CollectIntegers>,
         ) -> Result<Option<VisitInterrupt>> {
             let state = visitor.state_mut();
@@ -122,10 +122,18 @@ fn 
composed_policies_share_array_scope_with_visit_and_walk_callbacks() {
     for order in [WalkOrder::PreOrder, WalkOrder::PostOrder] {
         let mut walker =
             WalkWithContextPolicy::new(CollectIntegers::default(), 
(ArrayScope, RecordDescent));
-        assert!(walker.walk(&root, order).unwrap().is_none());
-        assert_eq!(walker.state().integers, expected);
-        assert_eq!(walker.state().depth, 0);
-        assert_eq!(walker.state().descent_depths, vec![1, 1, 2, 2, 2, 1]);
+        for run in 1..=2 {
+            assert!(structural_walk(&root, &mut walker, order)
+                .unwrap()
+                .is_none());
+            assert_eq!(walker.state().integers, expected.repeat(run));
+            assert_eq!(walker.state().depth, 0);
+            assert_eq!(
+                walker.state().descent_depths,
+                [1, 1, 2, 2, 2, 1].repeat(run)
+            );
+        }
+        assert!(structural_walk(&root, walker, order).unwrap().is_none());
     }
 }
 
@@ -170,7 +178,7 @@ fn 
policy_continuation_scopes_regions_and_restores_after_halts() {
     impl ContextPolicy<Probe> for Scope {
         fn default_visit(
             &self,
-            value: &VisitValue,
+            value: &StructuralView,
             ctx: &mut VisitContext<'_, Probe>,
         ) -> Result<Option<VisitInterrupt>> {
             let Some(array) = value.cast::<Array<i64>>() else {
@@ -197,7 +205,12 @@ fn 
policy_continuation_scopes_regions_and_restores_after_halts() {
             let policies = (Scope(Simple, 4), (Scope(Pattern, 3), Scope(Use, 
2)));
             let (result, state) = if let Some(order) = order {
                 let mut walker = WalkWithContextPolicy::new(state, policies);
-                let result = walker.walk(&root, order);
+                let result = structural_walk(&root, &mut walker, order);
+                // Reuse the same borrowed walker after completion, 
interruption,
+                // or error. The new root must begin outside every prior 
region.
+                assert!(structural_walk(&99_i64, &mut walker, order)
+                    .unwrap()
+                    .is_none());
                 (result, walker.into_state())
             } else {
                 let mut visitor =
@@ -227,6 +240,9 @@ fn 
policy_continuation_scopes_regions_and_restores_after_halts() {
             }
             // Unwind the innermost, middle, and outer scopes.
             expected.extend([(2, Pattern), (3, Simple), (4, Use)]);
+            if order.is_some() {
+                expected.push((99, Use));
+            }
             assert_eq!(state.seen, expected);
         }
     }
@@ -280,7 +296,7 @@ fn 
policy_continuation_retargets_without_dispatching_the_container() {
     impl ContextPolicy<Probe> for Redirect {
         fn default_visit(
             &self,
-            value: &VisitValue,
+            value: &StructuralView,
             ctx: &mut VisitContext<'_, Probe>,
         ) -> Result<Option<VisitInterrupt>> {
             let Some(array) = value.cast::<Array<Any>>() else {
@@ -307,7 +323,7 @@ fn 
policy_continuation_retargets_without_dispatching_the_container() {
     impl ContextPolicy<Probe> for Observe {
         fn default_visit(
             &self,
-            value: &VisitValue,
+            value: &StructuralView,
             ctx: &mut VisitContext<'_, Probe>,
         ) -> Result<Option<VisitInterrupt>> {
             if let Some(array) = value.cast::<Array<Any>>() {
@@ -334,7 +350,9 @@ fn 
policy_continuation_retargets_without_dispatching_the_container() {
     for order in [WalkOrder::PreOrder, WalkOrder::PostOrder] {
         for skip in [false, true] {
             let mut walker = WalkWithContextPolicy::new(Probe(vec![], skip), 
(Redirect, Observe));
-            assert!(walker.walk(&root, order).unwrap().is_none());
+            assert!(structural_walk(&root, &mut walker, order)
+                .unwrap()
+                .is_none());
             let mut expected = descent.clone();
             let array_callback = ("array callback", 2, DefRegionKind::None);
             match order {
@@ -351,7 +369,7 @@ fn 
policy_continuation_retargets_without_dispatching_the_container() {
     }
     let mut visitor = VisitCallbacks::new(
         Probe::default(),
-        |value: &VisitValue, ctx: &mut VisitContext<'_, Probe>| {
+        |value: &StructuralView, ctx: &mut VisitContext<'_, Probe>| {
             let kind = ctx.def_region_kind();
             if let Some(array) = value.cast::<Array<Any>>() {
                 ctx.state_mut()
@@ -377,7 +395,7 @@ fn 
policy_continuation_retargets_without_dispatching_the_container() {
     let target = visit_region_graph(false);
     let mut visitor = VisitCallbacks::new(
         Probe::default(),
-        |value: &VisitValue, ctx: &mut VisitContext<'_, Probe>| {
+        |value: &StructuralView, ctx: &mut VisitContext<'_, Probe>| {
             if value.cast::<i64>() == Some(-1) {
                 assert!(ctx
                     .default_visit_children(&Any::new(), 
DefRegionKind::Pattern)?
@@ -416,7 +434,7 @@ fn 
policy_halts_skip_remaining_policies_and_restore_outer_state() {
     }
     #[dispatch(walk)]
     impl Probe {
-        fn walk_any(&mut self, value: &VisitValue, kind: DefRegionKind) -> 
WalkResult {
+        fn walk_any(&mut self, value: &StructuralView, kind: DefRegionKind) -> 
WalkResult {
             assert!(
                 value.cast::<Array<i64>>().is_some(),
                 "children must not be visited"
@@ -430,7 +448,7 @@ fn 
policy_halts_skip_remaining_policies_and_restore_outer_state() {
     impl ContextPolicy<Probe> for Scope {
         fn default_visit(
             &self,
-            value: &VisitValue,
+            value: &StructuralView,
             ctx: &mut VisitContext<'_, Probe>,
         ) -> Result<Option<VisitInterrupt>> {
             ctx.state_mut().events.push("enter");
@@ -446,7 +464,7 @@ fn 
policy_halts_skip_remaining_policies_and_restore_outer_state() {
     impl ContextPolicy<Probe> for Stop {
         fn default_visit(
             &self,
-            _: &VisitValue,
+            _: &StructuralView,
             ctx: &mut VisitContext<'_, Probe>,
         ) -> Result<Option<VisitInterrupt>> {
             assert_eq!(ctx.def_region_kind(), DefRegionKind::Pattern);
@@ -463,7 +481,7 @@ fn 
policy_halts_skip_remaining_policies_and_restore_outer_state() {
     impl ContextPolicy<Probe> for Unreachable {
         fn default_visit(
             &self,
-            _: &VisitValue,
+            _: &StructuralView,
             _: &mut VisitContext<'_, Probe>,
         ) -> Result<Option<VisitInterrupt>> {
             panic!("the policy after Stop must not run")
@@ -475,12 +493,12 @@ fn 
policy_halts_skip_remaining_policies_and_restore_outer_state() {
             let policies = (Scope, (Stop(error), Unreachable));
             let (result, state) = if let Some(order) = order {
                 let mut walker = WalkWithContextPolicy::new(Probe::default(), 
policies);
-                let result = walker.walk(&root, order);
+                let result = structural_walk(&root, &mut walker, order);
                 (result, walker.into_state())
             } else {
                 let mut visitor = VisitCallbacks::new(
                     Probe::default(),
-                    |value: &VisitValue, ctx: &mut VisitContext<'_, Probe>| {
+                    |value: &StructuralView, ctx: &mut VisitContext<'_, 
Probe>| {
                         let kind = ctx.def_region_kind();
                         ctx.state_mut().walk_any(value, kind);
                         ctx.visit_children()
@@ -512,7 +530,7 @@ fn 
policy_regions_compose_with_field_flags_and_function_hooks() {
     impl ContextPolicy<PolicyRegionTrace> for SetRootRegion {
         fn default_visit(
             &self,
-            value: &VisitValue,
+            value: &StructuralView,
             ctx: &mut VisitContext<'_, PolicyRegionTrace>,
         ) -> Result<Option<VisitInterrupt>> {
             if value.type_index() == self.0 {
@@ -551,7 +569,9 @@ fn 
policy_regions_compose_with_field_flags_and_function_hooks() {
                 let state = if let Some(order) = order {
                     let mut walker =
                         
WalkWithContextPolicy::new(PolicyRegionTrace::default(), policy);
-                    assert!(walker.walk(&root, order).unwrap().is_none());
+                    assert!(structural_walk(&root, &mut walker, order)
+                        .unwrap()
+                        .is_none());
                     walker.into_state()
                 } else {
                     let mut visitor = VisitCallbacks::new(
@@ -578,7 +598,7 @@ fn 
walk_policy_preserves_reflected_pattern_before_default_descent() {
     impl ContextPolicy<PolicyRegionTrace> for Reenter {
         fn default_visit(
             &self,
-            value: &VisitValue,
+            value: &StructuralView,
             ctx: &mut VisitContext<'_, PolicyRegionTrace>,
         ) -> Result<Option<VisitInterrupt>> {
             match value.cast::<i64>() {
@@ -602,7 +622,9 @@ fn 
walk_policy_preserves_reflected_pattern_before_default_descent() {
         for order in [WalkOrder::PreOrder, WalkOrder::PostOrder] {
             let mut walker =
                 WalkWithContextPolicy::new(PolicyRegionTrace::default(), 
Reenter(requested));
-            assert!(walker.walk(&root, order).unwrap().is_none());
+            assert!(structural_walk(&root, &mut walker, order)
+                .unwrap()
+                .is_none());
             let expected = match order {
                 WalkOrder::PreOrder => vec![
                     (1, Simple),
@@ -670,7 +692,7 @@ fn plain_walk_uses_registered_array_hook() {
     let mut integers = 0;
     assert!(structural_walk(
         &root,
-        |value: &VisitValue| {
+        |value: &StructuralView| {
             if value.cast::<i64>().is_some() {
                 integers += 1;
             }
@@ -692,7 +714,7 @@ fn plain_walk_visits_map_values_without_visiting_keys() {
     let mut strings = 0;
     assert!(structural_walk(
         &root,
-        |value: &VisitValue| {
+        |value: &StructuralView| {
             if value.cast::<i64>().is_some() {
                 integers += 1;
             } else if value.cast::<FfiString>().is_some() {
@@ -716,7 +738,7 @@ fn primitive_values_are_leaves_in_pre_and_post_order() {
     let mut pre = Vec::new();
     assert!(structural_walk(
         &dtype,
-        |value: &VisitValue| {
+        |value: &StructuralView| {
             if value.cast::<DLDataType>().is_some() {
                 pre.push("dtype");
             } else if value.cast::<i64>().is_some() {
@@ -733,7 +755,7 @@ fn primitive_values_are_leaves_in_pre_and_post_order() {
     let mut skipped = Vec::new();
     assert!(structural_walk(
         &dtype,
-        |value: &VisitValue| {
+        |value: &StructuralView| {
             if value.cast::<DLDataType>().is_some() {
                 skipped.push("dtype");
                 WalkResult::Skip
@@ -751,7 +773,7 @@ fn primitive_values_are_leaves_in_pre_and_post_order() {
     let mut post = Vec::new();
     assert!(structural_walk(
         &dtype,
-        |value: &VisitValue| {
+        |value: &StructuralView| {
             if value.cast::<DLDataType>().is_some() {
                 post.push("dtype");
             } else if value.cast::<i64>().is_some() {
@@ -771,7 +793,7 @@ fn primitive_fast_path_preserves_none_interrupt_and_error() 
{
     let mut none_calls = 0;
     assert!(structural_walk(
         &Any::new(),
-        |_value: &VisitValue| {
+        |_value: &StructuralView| {
             none_calls += 1;
             WalkResult::Advance
         },
@@ -811,7 +833,7 @@ fn 
registered_map_hook_visits_all_values_without_visiting_keys() {
     let mut strings = 0;
     assert!(structural_walk(
         &root,
-        |value: &VisitValue| {
+        |value: &StructuralView| {
             if let Some(integer) = value.cast::<i64>() {
                 sum += integer;
             } else if value.cast::<FfiString>().is_some() {
@@ -834,7 +856,7 @@ fn interrupt_payload_crosses_map_traversal() {
         .collect();
     let outcome = structural_walk(
         &root,
-        |value: &VisitValue| {
+        |value: &StructuralView| {
             if value.cast::<i64>().is_some() {
                 return WalkResult::interrupt_with(99i64);
             }
@@ -854,7 +876,7 @@ fn handler_error_crosses_map_traversal() {
     let root: Map<FfiString, i64> = [(FfiString::from("a"), 
1i64)].into_iter().collect();
     let error = match structural_walk(
         &root,
-        |value: &VisitValue| -> Result<WalkResult> {
+        |value: &StructuralView| -> Result<WalkResult> {
             if value.cast::<i64>().is_some() {
                 Err(runtime_error("map handler failed"))
             } else {
@@ -876,7 +898,7 @@ fn interrupt_stops_without_running_remaining_callbacks() {
     let mut integers = 0;
     let outcome = structural_walk(
         &root,
-        |value: &VisitValue| {
+        |value: &StructuralView| {
             if value.cast::<i64>().is_some() {
                 integers += 1;
                 return WalkResult::Interrupt;
@@ -901,7 +923,7 @@ struct ManualRegionVisitor {
 impl StructuralVisitor for ManualRegionVisitor {
     fn visit(
         &mut self,
-        value: &VisitValue,
+        value: &StructuralView,
         def_region_kind: DefRegionKind,
     ) -> Result<Option<VisitInterrupt>> {
         if let Some(array) = value.cast::<Array<i64>>() {
@@ -1047,7 +1069,7 @@ struct StraddleVisitor {
 impl StructuralVisitor for StraddleVisitor {
     fn visit(
         &mut self,
-        value: &VisitValue,
+        value: &StructuralView,
         def_region_kind: DefRegionKind,
     ) -> Result<Option<VisitInterrupt>> {
         let label = match value.cast::<i64>() {
@@ -1119,7 +1141,7 @@ fn nested_walk_restores_the_outer_active_visitor() {
 
     assert!(structural_walk(
         &outer,
-        |value: &VisitValue| -> Result<WalkResult> {
+        |value: &StructuralView| -> Result<WalkResult> {
             if let Some(value) = value.cast::<i64>() {
                 outer_values.push(value);
             }
@@ -1127,7 +1149,7 @@ fn nested_walk_restores_the_outer_active_visitor() {
                 entered_inner = true;
                 structural_walk(
                     &inner,
-                    |value: &VisitValue| {
+                    |value: &StructuralView| {
                         if let Some(value) = value.cast::<i64>() {
                             inner_values.push(value);
                         }
@@ -1151,7 +1173,7 @@ fn interrupt_payload_is_returned_to_the_caller() {
     let root = Array::new(vec![1i64, 2]);
     let outcome = structural_walk(
         &root,
-        |value: &VisitValue| {
+        |value: &StructuralView| {
             if value.cast::<i64>() == Some(1) {
                 return WalkResult::interrupt_with(42i64);
             }
@@ -1171,7 +1193,7 @@ fn handler_errors_include_native_visit_path() {
     let root = Array::new(vec![1i64]);
     let error = match structural_walk(
         &root,
-        |value: &VisitValue| -> Result<WalkResult> {
+        |value: &StructuralView| -> Result<WalkResult> {
             if value.cast::<i64>().is_some() {
                 Err(runtime_error("handler failed"))
             } else {
@@ -1194,7 +1216,7 @@ fn visitor_errors_include_native_visit_path() {
     impl StructuralVisitor for FailingVisitor {
         fn visit(
             &mut self,
-            value: &VisitValue,
+            value: &StructuralView,
             def_region_kind: DefRegionKind,
         ) -> Result<Option<VisitInterrupt>> {
             if value.cast::<i64>().is_some() {
@@ -1252,7 +1274,7 @@ fn 
visitor_interrupt_propagates_through_default_children() {
     impl StructuralVisitor for InterruptingVisitor {
         fn visit(
             &mut self,
-            value: &VisitValue,
+            value: &StructuralView,
             def_region_kind: DefRegionKind,
         ) -> Result<Option<VisitInterrupt>> {
             if value.cast::<i64>() == Some(2) {
@@ -1280,7 +1302,7 @@ fn closure_walk_receives_def_region_kind() {
     let mut kinds = Vec::new();
     assert!(structural_walk(
         &root,
-        |value: &VisitValue, kind: DefRegionKind| {
+        |value: &StructuralView, kind: DefRegionKind| {
             if value.cast::<i64>().is_some() {
                 kinds.push(kind);
             }
@@ -1299,7 +1321,7 @@ fn closure_walk_supports_post_order_and_skip() {
     let mut order_probe = Vec::new();
     assert!(structural_walk(
         &root,
-        |value: &VisitValue| {
+        |value: &StructuralView| {
             order_probe.push(value.cast::<i64>());
             WalkResult::Advance
         },
@@ -1312,7 +1334,7 @@ fn closure_walk_supports_post_order_and_skip() {
     let mut visited = 0;
     assert!(structural_walk(
         &root,
-        |value: &VisitValue| {
+        |value: &StructuralView| {
             visited += 1;
             if value.cast::<i64>().is_none() {
                 WalkResult::Skip
@@ -1366,7 +1388,7 @@ fn chain_links_may_mix_def_region_arity() {
                 kinds.push(kind);
                 WalkResult::Advance
             },
-            |_value: &VisitValue, kind: DefRegionKind| {
+            |_value: &StructuralView, kind: DefRegionKind| {
                 assert_eq!(kind, DefRegionKind::None);
                 objects += 1;
                 WalkResult::Advance
@@ -1412,7 +1434,7 @@ fn chain_link_errors_include_native_visit_path() {
         &root,
         (
             |_value: i64| -> Result<WalkResult> { Err(runtime_error("link 
failed")) },
-            |_value: &VisitValue| WalkResult::Advance,
+            |_value: &StructuralView| WalkResult::Advance,
         ),
         WalkOrder::PreOrder,
     ) {
@@ -1504,7 +1526,7 @@ fn chain_supports_full_arity() {
                 objects += 1;
                 WalkResult::Advance
             },
-            |_value: &VisitValue, _kind: DefRegionKind| {
+            |_value: &StructuralView, _kind: DefRegionKind| {
                 others += 1;
                 WalkResult::Advance
             },
@@ -1575,7 +1597,7 @@ struct InheritedRegionProbe {
 impl StructuralVisitor for InheritedRegionProbe {
     fn visit(
         &mut self,
-        value: &VisitValue,
+        value: &StructuralView,
         def_region_kind: DefRegionKind,
     ) -> Result<Option<VisitInterrupt>> {
         if self.at_root {
@@ -1681,12 +1703,12 @@ fn nested_tuple_chain_exceeds_flat_arity() {
                         objects += 1;
                         WalkResult::Advance
                     },
-                    (|_value: &VisitValue, _kind: DefRegionKind| {
+                    (|_value: &StructuralView, _kind: DefRegionKind| {
                         others += 1;
                         WalkResult::Advance
                     },),
                 ),
-                |_value: &VisitValue| WalkResult::Advance,
+                |_value: &StructuralView| WalkResult::Advance,
             ),
         ),
         WalkOrder::PreOrder,
@@ -1706,7 +1728,7 @@ fn nested_tuple_first_match_order_is_flattened() {
     assert!(structural_walk(
         &root,
         (
-            (|_value: &VisitValue| {
+            (|_value: &StructuralView| {
                 first += 1;
                 WalkResult::Advance
             },),
@@ -1799,7 +1821,7 @@ fn 
stateful_callback_visit_reborrows_visitor_during_recursion() {
     let root = Array::new(vec![Array::new(vec![1i64, 2])]);
     let mut visitor = VisitCallbacks::new(
         StatefulVisitDepth::default(),
-        |_value: &VisitValue, visitor: &mut VisitContext<'_, 
StatefulVisitDepth>| {
+        |_value: &StructuralView, visitor: &mut VisitContext<'_, 
StatefulVisitDepth>| {
             visitor.state_mut().current += 1;
             visitor.state_mut().calls += 1;
             let current = visitor.state().current;
@@ -1823,7 +1845,7 @@ fn 
callback_visit_can_reenter_the_same_fn_through_visitor() {
     let visits = Cell::new(0);
     assert!(structural_visit(
         &root,
-        |_value: &VisitValue, visitor: &mut VisitContext<'_, ()>| {
+        |_value: &StructuralView, visitor: &mut VisitContext<'_, ()>| {
             visits.set(visits.get() + 1);
             visitor.visit_children()
         },
@@ -1843,7 +1865,7 @@ fn 
callback_visit_tuple_is_first_match_and_can_interrupt() {
             |value: i64, _visitor: &mut VisitContext<'_, ()>| {
                 (value == 2).then(|| VisitInterrupt::with(value))
             },
-            |_value: &VisitValue, visitor: &mut VisitContext<'_, ()>| {
+            |_value: &StructuralView, visitor: &mut VisitContext<'_, ()>| {
                 fallback.set(fallback.get() + 1);
                 visitor.visit_children()
             },
@@ -1921,7 +1943,7 @@ fn 
nested_callback_visit_restores_the_outer_active_visitor() {
 
     assert!(structural_visit(
         &outer,
-        |value: &VisitValue, visitor: &mut VisitContext<'_, ()>| {
+        |value: &StructuralView, visitor: &mut VisitContext<'_, ()>| {
             if let Some(value) = value.cast::<i64>() {
                 outer_values.borrow_mut().push(value);
             }
diff --git a/rust/tvm-ffi/tests/test_structural_visitor_alignment.rs 
b/rust/tvm-ffi/tests/test_structural_visitor_alignment.rs
index 6724e9bd..b69e1167 100644
--- a/rust/tvm-ffi/tests/test_structural_visitor_alignment.rs
+++ b/rust/tvm-ffi/tests/test_structural_visitor_alignment.rs
@@ -27,8 +27,8 @@
 //! definition region, the rest inherit the surrounding state.
 
 use tvm_ffi::{
-    structural_visit, Array, DefRegionKind, Result, String as FfiString, 
StructuralVisitor,
-    VisitInterrupt, VisitValue,
+    structural_visit, Array, DefRegionKind, Result, String as FfiString, 
StructuralView,
+    StructuralVisitor, VisitInterrupt,
 };
 
 /// C++: class TestVisitorObj : public StructuralVisitorObj
@@ -49,7 +49,7 @@ impl StructuralVisitor for RecordingVisitor {
     /// `TFuncObj::StructuralVisit`.
     fn visit(
         &mut self,
-        value: &VisitValue,
+        value: &StructuralView,
         def_region_kind: DefRegionKind,
     ) -> Result<Option<VisitInterrupt>> {
         let integer = value.cast::<i64>();
@@ -103,9 +103,9 @@ fn records_values_and_def_region_modes() {
     assert_eq!(
         visitor.modes,
         vec![
-            DefRegionKind::None,      // the array itself
+            DefRegionKind::None,    // the array itself
             DefRegionKind::Pattern, // element 0: the "params" position
-            DefRegionKind::None,      // element 1: the "body" position
+            DefRegionKind::None,    // element 1: the "body" position
         ]
     );
 }

Reply via email to