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

tqchen 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 744cf65c [Rust][DOCS] Add std-style examples to the core API docs 
(#926)
744cf65c is described below

commit 744cf65c2d0186894ea23afcbafa9e1cfb943671
Author: Tianqi Chen <[email protected]>
AuthorDate: Wed Nov 4 19:44:54 2026 -0500

    [Rust][DOCS] Add std-style examples to the core API docs (#926)
    
    This PR adds std-style rustdoc to the core Rust APIs. The key top-level
    APIs (types, entry functions and macros) carry runnable examples.
    Second-level methods get a one-line summary, a short usage where it
    helps, and a see-also link to the related top-level API.
    
    - **Macros.** `#[dispatch]`, `match_any!`, `rs_object` and
    `def_object_ref!` are documented on their `tvm_ffi` re-exports, so their
    examples run as tvm-ffi doctests and the macro crate keeps no dependency
    on tvm-ffi. The `dispatch` and `match_any!` examples follow the Rust
    guide. The `rs_object` and `def_object_ref!` examples, previously
    `ignore`, now compile and run.
    - **Values.** `Any` and `AnyView` (with `try_as` and `downcast_ref`),
    `Unchanged` and `UnchangedOr`, and `ObjectRefCast::try_cast`, the Rust
    counterpart of C++ `ObjectRef::as<T>`.
    - **Traversal.** The `structural_mutate` example shows a handler that
    rewrites one field with `MutationView::field`. `MutationView`,
    `InplaceMode`, the visitor and mutator views, and `Dispatcher` point to
    the traversal entry points.
    - **Functions, errors and containers.** `Function` (`from_typed`,
    `from_packed`, `call_tuple`, `call_packed`, `get_global`,
    `register_global_typed`), `Error` and `ErrorKind`, `bail!`, `ensure!`,
    `attach_context!`, `Array`, `List`, `Map`, `String`, `Bytes`, `Shape`
    and `Tensor`.
    - **Macro paths.** The macros always emit `::tvm_ffi` paths, which
    `extern crate self as tvm_ffi` resolves inside tvm-ffi, so the same
    expansion works in the crate, its doctests and dependent crates.
    `attach_context!` no longer needs `Error` in scope.
---
 rust/tvm-ffi-macros/src/lib.rs              | 160 +---------------
 rust/tvm-ffi-macros/src/utils.rs            |  10 +-
 rust/tvm-ffi/src/any.rs                     | 161 ++++++++++++----
 rust/tvm-ffi/src/collections/array.rs       |  62 ++++++-
 rust/tvm-ffi/src/collections/list.rs        |  21 ++-
 rust/tvm-ffi/src/collections/map.rs         |  20 +-
 rust/tvm-ffi/src/collections/shape.rs       |  13 +-
 rust/tvm-ffi/src/collections/tensor.rs      |  15 +-
 rust/tvm-ffi/src/error.rs                   | 122 ++++++++++--
 rust/tvm-ffi/src/extra/dispatch.rs          |  18 +-
 rust/tvm-ffi/src/extra/structural_mutate.rs | 277 +++++++++++++++++++++++----
 rust/tvm-ffi/src/extra/structural_visit.rs  |  85 ++++++++-
 rust/tvm-ffi/src/extra/unchanged.rs         | 177 +++++++++++++++---
 rust/tvm-ffi/src/function.rs                | 278 ++++++++++++++++++++++------
 rust/tvm-ffi/src/lib.rs                     | 221 +++++++++++++++++++++-
 rust/tvm-ffi/src/macros.rs                  |  96 ++++++++--
 rust/tvm-ffi/src/object.rs                  |  28 ++-
 rust/tvm-ffi/src/string.rs                  |  29 ++-
 18 files changed, 1406 insertions(+), 387 deletions(-)

diff --git a/rust/tvm-ffi-macros/src/lib.rs b/rust/tvm-ffi-macros/src/lib.rs
index 5acdbd98..d6533795 100644
--- a/rust/tvm-ffi-macros/src/lib.rs
+++ b/rust/tvm-ffi-macros/src/lib.rs
@@ -27,71 +27,16 @@ mod object_macros;
 mod rs_object;
 mod utils;
 
-/// Generate source-ordered runtime type dispatch from `prefix_*` handlers.
-///
-/// `#[dispatch(prefix, (Carrier, Arg, ...), Ret)]` declares the complete input
-/// tuple and result type. The first input is `AnyView<'_>` or 
`MutationView<'_>`
-/// (including aliases); subsequent inputs are passed to matching handlers.
-/// The macro implements `Dispatcher<(Carrier, Arg, ...), Option<Ret>>` for the
-/// receiver reference (`&T` or `&mut T`). The inherent `.dispatch(value, ...)`
-/// method owns matching; `.dispatch_tuple((value, ...))` unpacks and forwards.
-/// Both methods always inline. A matched result is converted with `Into<Ret>`
-/// and wrapped in `Some`. When both the handler's return type and `Ret` are
-/// written as `Result<..>`, the success value is converted with `Into` 
instead,
-/// so a `Result<UnchangedOr<Expr>>` or `Result<i64>` handler fits a
-/// `Result<UnchangedOr<Any>>` dispatcher. Only the outer `None` means no 
match;
-/// matched errors and inner `None` are final. Conversion uses ordinary trait
-/// implementations, without recursively converting results or wrapping values
-/// in `Ok`.
-///
-/// Handlers match an owned FFI value, a borrowed object node, or an optional
-/// by-value `AnyView` catch-all, which must be last. Each handler receives its
-/// typed first value and may consume an initial prefix of the remaining 
declared
-/// arguments without cloning. Neither arguments nor results are deduced from a
-/// handler signature, and no handler needs to consume every declared argument.
-///
-/// Handlers share the same receiver and method generic declarations and 
bounds.
-/// Pattern-only, unbounded lifetimes may differ between handlers. Shared 
method
-/// generics must occur in the receiver, declared tuple or declared return 
type.
-/// Named lifetimes come from those shared declarations or the enclosing impl;
-/// use the same name in input and output to return a borrow of that input.
-/// Elided input lifetimes are independent; elided output lifetimes borrow the
-/// receiver. `Self` in the declared types denotes the original implementor.
-/// Handler-level `cfg`/`cfg_attr` attributes are unsupported; configure the 
whole
-/// impl instead. A consuming `MutationView` retains mutation permission; its
-/// typed misses recover the same handle, and `AnyView` cannot grant it.
-///
-/// Matching invokes the public `match_any!` macro and its exact-final-type
-/// lookup for eligible larger handler sets. Dispatch also matches scalar 
payloads
-/// and keeps owned argument conversions strict. Parameterized containers and
-/// other non-exact matchers retain ordered, fully checked conversion.
-///
-/// The macro operates on inherent impls only. Traversal engines explicitly
-/// call the dispatcher and configure their own default recursion;
-/// no method prefix has a reserved traversal meaning.
-///
-/// Generate one dispatcher per type. Method generics can select different
-/// argument and return types; use an expected result or explicit type 
arguments
-/// when inference is ambiguous.
+// Documented on the `tvm_ffi::dispatch` re-export, whose examples run as
+// tvm-ffi doctests.
 #[proc_macro_error]
 #[proc_macro_attribute]
 pub fn dispatch(attr: TokenStream, item: TokenStream) -> TokenStream {
     dispatch::dispatch(attr, item)
 }
 
-/// Match payloads in `Any`, `AnyView`, object references or `MutationView`.
-///
-/// Write `Type(value)` to bind an owned or scalar result, `NodeObj(ref node)`
-/// to borrow an object node, or `MutationView<Type>(capability)` to transfer a
-/// mutation capability. Rust type aliases and qualified types are accepted.
-/// Arms are tried in order; end with an unguarded `_ => ...` fallback.
-///
-/// The input is evaluated and taken by value once. Use `&value` to retain its
-/// owner. Matching is strict: it does not coerce between value categories or
-/// truncate stored numeric values. Guards may reject a match and try the next 
arm.
-///
-/// Typed mutation capabilities do not support Map payloads, including nested
-/// Maps; use `MutationView<Any>` for those. Ordinary `Map<K, V>` arms are 
supported.
+// Documented on the `tvm_ffi::match_any` re-export, whose examples run as
+// tvm-ffi doctests.
 #[proc_macro_error]
 #[proc_macro]
 pub fn match_any(input: TokenStream) -> TokenStream {
@@ -166,105 +111,16 @@ pub fn derive_object_ref(input: TokenStream) -> 
TokenStream {
     object_macros::derive_object_ref(input)
 }
 
-/// Define an object type in Rust from its object struct, as Python's
-/// `py_class` does: the type registers its key with the reflection of every
-/// field, so that C++ structural equality and hashing, `repr`, deep copy and
-/// Python see the fields.
-///
-/// ```ignore
-/// use tvm_ffi::{def_object_ref, rs_object};
-///
-/// /// A pair of values under a `Base`.
-/// #[rs_object("my_ext.Pair", final)]
-/// pub struct PairObj {
-///     base: BaseObj,
-///     pub first: tvm_ffi::String,
-///     pub second: tvm_ffi::String,
-///     #[reflect(seq_hash_ignore)]
-///     pub note: tvm_ffi::String,
-/// }
-///
-/// def_object_ref!(pub Pair, Base, PairObj);
-///
-/// let pair = Pair::from(PairObj::new(BaseObj::new(/* ... */), "a", "b", ""));
-/// let base = Base::from(pair);
-/// ```
-///
-/// The first field, `base`, is the parent's object, as the base class of a C++
-/// object is: `base: Object` for a type under `ffi.Object`. The parent's 
object
-/// must implement `ShallowCopy` and [`UnsafeInit`], as `rs_object` types and
-/// non-final C++ types bound by `tvm-ffi-stubgen` do. Every other field is
-/// reflected; `#[reflect(writable, seq_hash_ignore, ...)]` gives a field the
-/// [`FieldFlags`] of those names, and `name` and `doc` as for
-/// `derive(Object)`. The macro adds to the struct:
-///
-/// - `#[repr(C)]` and `Deref` to the parent's object, so `obj.field` reads the
-///   fields of both.
-/// - `PairObj::new(base, ...)`, which builds the object from the parent's
-///   object and the fields in order: a scalar (`bool`, an integer or a float)
-///   as its own type, so that a literal infers as it, and any other field from
-///   anything that converts into it, as the `new` of a type bound by
-///   `tvm-ffi-stubgen` takes them.
-///
-/// [`def_object_ref!`] defines the reference to it and its conversion into the
-/// reference to its parent.
-///
-/// After the type key:
-///
-/// - `final`, `s_eq_hash_kind = "TreeNode"`, `child_slots = N`,
-///   `child_slots_can_overflow = bool` and `reflection = path` are
-///   `derive(Object)`'s `#[type_final]`, `#[type_s_eq_hash_kind]`,
-///   `#[type_child_slots]`, `#[type_child_slots_can_overflow]` and
-///   `#[type_reflection]`.
-/// - `crate = path` is where the generated code finds tvm-ffi
-///   (`crate = ::my_lib::tvm_ffi`), for a crate that depends on it only 
through
-///   a re-export; by default `tvm_ffi`.
-///
-/// The type is a `derive(Object)` type with `#[type_register]`: it registers 
on
-/// its first `type_index()`, with the fields, the methods,
-/// `__ffi_shallow_copy__` and `__ffi_type_final__`, and a creator that makes
-/// the object with every field undefined for reflection to set, as C++ makes 
one
-/// with `UnsafeInit`, from which it registers `__ffi_new__` and, unless the
-/// methods define it, `__ffi_init__`; the macro implements [`UnsafeInit`] for
-/// the object. A field's type must implement `AnyCompatible` and `Clone`; an
-/// `init_off` field's type must take all-zero bytes as a value, as
-/// `__ffi_init__` leaves the field so.
-///
-/// [`FieldFlags`]: ../tvm_ffi/reflection/struct.FieldFlags.html
-/// [`UnsafeInit`]: ../tvm_ffi/reflection/trait.UnsafeInit.html
+// Documented on the `tvm_ffi::rs_object` re-export, whose examples run as
+// tvm-ffi doctests.
 #[proc_macro_error]
 #[proc_macro_attribute]
 pub fn rs_object(args: TokenStream, item: TokenStream) -> TokenStream {
     rs_object::rs_object(args, item)
 }
 
-/// Define the reference to an object type, as C++
-/// `TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE` does:
-/// `def_object_ref!(TypeName, ParentType, ObjectName)`.
-///
-/// ```ignore
-/// use tvm_ffi::def_object_ref;
-///
-/// def_object_ref!(
-///     /// A pair of values.
-///     pub Pair,
-///     Base,
-///     PairObj
-/// );
-/// ```
-///
-/// `Pair` holds a `PairObj` and is a `derive(ObjectRef)` type. It dereferences
-/// to `PairObj`, and a `PairObj` converts into it.
-///
-/// `ParentType` is the reference to the parent's object: `Base` here, and
-/// `tvm_ffi::object::ObjectRef` for a type under `ffi.Object`. `Pair` converts
-/// into it by `impl_object_upcast!`, so its object must be a registered base 
of
-/// `PairObj`, as that macro requires. A conversion into the reference to a
-/// further ancestor takes its own `impl_object_upcast!`.
-///
-/// Attributes and visibility before the name go on the struct. `crate = path`
-/// after the object type is where the generated code finds tvm-ffi, as for
-/// [`rs_object`](macro@rs_object).
+// Documented on the `tvm_ffi::def_object_ref` re-export, whose examples run as
+// tvm-ffi doctests.
 #[proc_macro_error]
 #[proc_macro]
 pub fn def_object_ref(input: TokenStream) -> TokenStream {
diff --git a/rust/tvm-ffi-macros/src/utils.rs b/rust/tvm-ffi-macros/src/utils.rs
index 76fdf3fb..5b2abfe2 100644
--- a/rust/tvm-ffi-macros/src/utils.rs
+++ b/rust/tvm-ffi-macros/src/utils.rs
@@ -18,15 +18,13 @@
  */
 use proc_macro2::TokenStream;
 use quote::quote;
-use std::env;
 
 /// Return the path used to reference the `tvm-ffi` crate in generated code.
+///
+/// tvm-ffi declares `extern crate self as tvm_ffi`, so the same path works in
+/// the crate itself, in its doctests and in dependent crates.
 pub(crate) fn get_tvm_ffi_crate() -> TokenStream {
-    if env::var("CARGO_CRATE_NAME").as_deref() == Ok("tvm_ffi") {
-        quote!(crate)
-    } else {
-        quote!(::tvm_ffi)
-    }
+    quote!(::tvm_ffi)
 }
 
 /// Get an attribute by name from a derive input
diff --git a/rust/tvm-ffi/src/any.rs b/rust/tvm-ffi/src/any.rs
index 47c97b79..f9b24672 100644
--- a/rust/tvm-ffi/src/any.rs
+++ b/rust/tvm-ffi/src/any.rs
@@ -22,7 +22,25 @@ use crate::type_traits::AnyCompatible;
 use tvm_ffi_sys::TVMFFITypeIndex as TypeIndex;
 use tvm_ffi_sys::{TVMFFIAny, TVMFFIAnyViewToOwnedAny};
 
-/// Unmanaged Any that can hold reference to values
+/// A borrowed, type-erased FFI value.
+///
+/// `AnyView` holds any FFI value, such as an integer, a string or an object,
+/// without owning it: it does not retain objects, and its lifetime `'a` keeps
+/// it from outliving the value it borrows. It is `Copy` and is the argument
+/// type of FFI calls. Convert it into an [`Any`] to own the value.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_ffi::{Any, AnyView, Array};
+///
+/// let array = Array::new(vec![1_i64, 2, 3]);
+/// let view = AnyView::from(&array);
+/// assert_eq!(view.try_as::<Array<i64>>().unwrap().len(), 3);
+///
+/// let owned = Any::from(view);
+/// assert_eq!(owned.try_as::<Array<i64>>().unwrap().len(), 3);
+/// ```
 #[derive(Copy, Clone)]
 #[repr(C)]
 pub struct AnyView<'a> {
@@ -31,7 +49,26 @@ pub struct AnyView<'a> {
     _phantom: std::marker::PhantomData<&'a ()>,
 }
 
-/// Managed Any that can hold reference to values
+/// An owned, type-erased FFI value.
+///
+/// `Any` holds any FFI value, such as an integer, a string or an object, and
+/// owns it: cloning an `Any` that holds an object retains the object, and
+/// dropping it releases the object. Any [`AnyCompatible`] value converts into
+/// it with `From`; [`try_as`](Self::try_as) and 
[`match_any!`](crate::match_any)
+/// read it back. Borrow it as an [`AnyView`] to pass it without retaining.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_ffi::Any;
+///
+/// let value = Any::from(42_i64);
+/// assert_eq!(value.try_as::<i64>(), Some(42));
+/// assert_eq!(value.try_as::<tvm_ffi::String>(), None);
+///
+/// let text = Any::from(tvm_ffi::String::from("hello"));
+/// assert_eq!(text.try_as::<tvm_ffi::String>().unwrap().as_str(), "hello");
+/// ```
 #[repr(C)]
 pub struct Any {
     data: TVMFFIAny,
@@ -41,6 +78,11 @@ pub struct Any {
 // AnyView
 //---------------------
 impl<'a> AnyView<'a> {
+    /// Create a view of `None`, the empty FFI value.
+    ///
+    /// # Returns
+    ///
+    /// A view holding `None`.
     pub fn new() -> Self {
         Self {
             data: TVMFFIAny::new(),
@@ -74,6 +116,14 @@ impl<'a> AnyView<'a> {
         unsafe { Self::from_raw_ffi_any(data) }
     }
 
+    /// Return the runtime type index of the viewed value.
+    ///
+    /// Indices below `TypeIndex::kTVMFFIStaticObjectBegin` are the built-in
+    /// non-object types; the others are object types.
+    ///
+    /// # Returns
+    ///
+    /// The type index.
     #[inline(always)]
     pub fn type_index(&self) -> i32 {
         self.data.type_index
@@ -108,10 +158,16 @@ impl<'a> AnyView<'a> {
 
     /// Borrow the viewed object as `T` when its runtime type is `T` or a 
subtype.
     ///
-    /// `T` is an object representation type such as `MyPairObj`. Non-object
-    /// values and unrelated object types return `None`. This does not change
-    /// the object's reference count; the borrow cannot outlive this view or
-    /// the owner it borrows. See [`Any::downcast_ref`] for an example.
+    /// `T` is an object representation type such as `ArrayObj`. Non-object 
values
+    /// and unrelated object types return `None`. This does not change the 
object's
+    /// reference count; the borrow cannot outlive this view or the owner it
+    /// borrows.
+    ///
+    /// # Returns
+    ///
+    /// The borrowed object, or `None`.
+    ///
+    /// See also [`Any::downcast_ref`].
     #[inline(always)]
     pub fn downcast_ref<T: object::ObjectCore>(&self) -> Option<&T> {
         if self.data.type_index < TypeIndex::kTVMFFIStaticObjectBegin as i32
@@ -124,14 +180,23 @@ impl<'a> AnyView<'a> {
         Some(unsafe { &*self.data.data_union.v_obj.cast::<T>() })
     }
 
-    /// More strict version than try_from/try_into
+    /// Return the viewed value as a `T` when it is stored as exactly `T`.
+    ///
+    /// The check is strict and never converts: a stored integer is not 
returned as
+    /// `f64`, and an object is returned only as a reference type it is an 
instance
+    /// of. On success, `Any::from` of the result holds the same value.
     ///
-    /// This function will not try to cast the type
-    /// and ensures invariance that the return value is only Some(T)
-    /// Any::from(T) contains exactly the same value value
+    /// # Returns
     ///
-    /// will return Some(T) if the type is exactly compatible with T
-    /// will return None if the type is not compatible with T
+    /// The value, or `None` when it is not stored as `T`.
+    ///
+    /// ```
+    /// use tvm_ffi::AnyView;
+    /// assert_eq!(AnyView::from(&7_i64).try_as::<i64>(), Some(7));
+    /// assert_eq!(AnyView::from(&7_i64).try_as::<f64>(), None);
+    /// ```
+    ///
+    /// See also [`Any::try_as`].
     #[inline(always)]
     pub fn try_as<T>(&self) -> Option<T>
     where
@@ -220,46 +285,53 @@ impl<T> TryFromTemp<T> {
 // Any
 //---------------------
 impl Any {
+    /// Create an `Any` holding `None`, the empty FFI value.
+    ///
+    /// # Returns
+    ///
+    /// An `Any` holding `None`.
     #[inline(always)]
     pub fn new() -> Self {
         Self {
             data: TVMFFIAny::new(),
         }
     }
+
+    /// Return the runtime type index of the stored value.
+    ///
+    /// Indices below `TypeIndex::kTVMFFIStaticObjectBegin` are the built-in
+    /// non-object types; the others are object types.
+    ///
+    /// # Returns
+    ///
+    /// The type index.
     #[inline(always)]
     pub fn type_index(&self) -> i32 {
         self.data.type_index
     }
+
     #[inline(always)]
     pub(crate) fn as_raw_ffi_any(&self) -> &TVMFFIAny {
         &self.data
     }
     /// Borrow the stored object as `T` when its runtime type is `T` or a 
subtype.
     ///
-    /// `T` is an object representation type such as `FooObj`, rather than its
-    /// managed reference `Foo`. Non-object values and unrelated object types
+    /// `T` is an object representation type such as `ArrayObj`, rather than 
its
+    /// managed reference `Array`. Non-object values and unrelated object types
     /// return `None`. This neither consumes `self` nor changes the object's
     /// reference count, and the returned reference cannot outlive `self`.
     ///
+    /// # Returns
+    ///
+    /// The borrowed object, or `None`.
+    ///
     /// ```
-    /// use tvm_ffi::{def_object_ref, rs_object, Any, AnyView, Object, 
ObjectRefCore};
-    /// use tvm_ffi::object::ObjectRef;
-    ///
-    /// #[rs_object("my.MyPair", crate = tvm_ffi)]
-    /// pub struct MyPairObj {
-    ///     base: Object,
-    ///     pub first: i64,
-    ///     pub second: i64,
-    /// }
-    /// def_object_ref!(pub MyPair, ObjectRef, MyPairObj, crate = tvm_ffi);
-    ///
-    /// let pair = MyPair::from(MyPairObj::new(Object::new(), 3, 4));
-    /// assert_eq!(pair.downcast_ref::<MyPairObj>().unwrap().first, 3);
-    /// let value = Any::from(pair);
-    /// assert_eq!(value.downcast_ref::<MyPairObj>().unwrap().second, 4);
-    /// let view = AnyView::from(&value);
-    /// assert_eq!(view.downcast_ref::<MyPairObj>().unwrap().first, 3);
+    /// use tvm_ffi::{collections::array::ArrayObj, Any, Array};
+    /// let value = Any::from(Array::new(vec![1_i64, 2]));
+    /// assert_eq!(value.downcast_ref::<ArrayObj>().unwrap().size, 2);
     /// ```
+    ///
+    /// See also [`try_as`](Self::try_as).
     #[inline]
     pub fn downcast_ref<T: object::ObjectCore>(&self) -> Option<&T> {
         if self.data.type_index < TypeIndex::kTVMFFIStaticObjectBegin as i32
@@ -272,18 +344,27 @@ impl Any {
         Some(unsafe { &*self.data.data_union.v_obj.cast::<T>() })
     }
 
-    /// Try to query if stored typed in Any exactly matches the type T
+    /// Return the stored value as a `T` when it is stored as exactly `T`.
     ///
-    /// This function is fast in the case of failure and can be used to check
-    /// if the type is compatible with T
+    /// The check is strict and never converts: a stored integer is not 
returned as
+    /// `f64`, and an object is returned only as a reference type it is an 
instance
+    /// of. On success, `Any::from` of the result holds the same value. A 
failed
+    /// check is cheap, so `try_as` also serves as a type test. A returned 
object
+    /// reference is retained; use [`downcast_ref`](Self::downcast_ref) to 
borrow the
+    /// object instead.
     ///
-    /// This function will not try to cast the type
-    /// and ensures invariance that the return value is only Some(T)
-    /// `Any::from(T)` contains exactly the same value value
-    /// `Any::try_as<T>()` contains exactly the same value value
+    /// # Returns
+    ///
+    /// The value, or `None` when it is not stored as `T`.
+    ///
+    /// ```
+    /// use tvm_ffi::Any;
+    /// let value = Any::from(7_i64);
+    /// assert_eq!(value.try_as::<i64>(), Some(7));
+    /// assert_eq!(value.try_as::<f64>(), None);
+    /// ```
     ///
-    /// will return Some(T) if the type is exactly compatible with T
-    /// will return None if the type is not compatible with T
+    /// See also [`match_any!`](crate::match_any).
     #[inline]
     pub fn try_as<T>(&self) -> Option<T>
     where
diff --git a/rust/tvm-ffi/src/collections/array.rs 
b/rust/tvm-ffi/src/collections/array.rs
index d4d60c7a..f7fb613b 100644
--- a/rust/tvm-ffi/src/collections/array.rs
+++ b/rust/tvm-ffi/src/collections/array.rs
@@ -50,6 +50,28 @@ unsafe impl ObjectCoreWithExtraItems for ArrayObj {
     }
 }
 
+/// An immutable, reference-counted sequence of `T`, shared with C++ and
+/// Python as `ffi.Array`.
+///
+/// Cloning an `Array` is cheap: it shares the same elements. Use [`List`]
+/// for a sequence that changes in place.
+///
+/// [`List`]: crate::List
+///
+/// # Examples
+///
+/// ```
+/// use tvm_ffi::Array;
+///
+/// let array = Array::new(vec![1_i64, 2, 3]);
+/// assert_eq!(array.len(), 3);
+/// assert_eq!(array.get(1)?, 2);
+/// assert!(array.get(3).is_err());
+///
+/// let doubled: Array<i64> = array.iter().map(|x| x * 2).collect();
+/// assert_eq!(doubled.iter().collect::<Vec<_>>(), vec![2, 4, 6]);
+/// # Ok::<(), tvm_ffi::Error>(())
+/// ```
 #[repr(C)]
 #[derive(Clone)]
 pub struct Array<T: ContainerElement + Clone> {
@@ -91,7 +113,15 @@ unsafe impl<T: ContainerElement + Clone> ObjectRefCore for 
Array<T> {
 }
 
 impl<T: ContainerElement + Clone> Array<T> {
-    /// Creates a new Array from a vector of items.
+    /// Create an array holding `items`.
+    ///
+    /// # Arguments
+    ///
+    /// * `items` - The elements.
+    ///
+    /// # Returns
+    ///
+    /// The array.
     pub fn new(items: Vec<T>) -> Self {
         let capacity = items.len();
         Self::new_with_capacity(items, capacity)
@@ -127,15 +157,38 @@ impl<T: ContainerElement + Clone> Array<T> {
         unsafe { Self::from_data(arc) }
     }
 
+    /// Return the number of elements.
+    ///
+    /// # Returns
+    ///
+    /// The number of elements.
     pub fn len(&self) -> usize {
         self.data.size as usize
     }
 
+    /// Return `true` if the array has no elements.
+    ///
+    /// # Returns
+    ///
+    /// `true` if the array has no elements.
     pub fn is_empty(&self) -> bool {
         self.len() == 0
     }
 
-    /// Retrieves an item at the given index.
+    /// Return the element at `index`.
+    ///
+    /// # Arguments
+    ///
+    /// * `index` - The position of the element.
+    ///
+    /// # Returns
+    ///
+    /// The element at `index`.
+    ///
+    /// # Errors
+    ///
+    /// Returns an `IndexError` if `index` is out of bounds, or a `TypeError`
+    /// if the element is not a `T`.
     pub fn get(&self, index: usize) -> Result<T, crate::Error> {
         if index >= self.len() {
             crate::bail!(crate::error::INDEX_ERROR, "Array get index out of 
bound");
@@ -157,6 +210,11 @@ impl<T: ContainerElement + Clone> Array<T> {
         }
     }
 
+    /// Return an iterator over the elements.
+    ///
+    /// # Returns
+    ///
+    /// An iterator over the elements.
     pub fn iter(&'_ self) -> ArrayIterator<'_, T> {
         ArrayIterator {
             array: self,
diff --git a/rust/tvm-ffi/src/collections/list.rs 
b/rust/tvm-ffi/src/collections/list.rs
index a0151a80..40a0c8b4 100644
--- a/rust/tvm-ffi/src/collections/list.rs
+++ b/rust/tvm-ffi/src/collections/list.rs
@@ -120,9 +120,24 @@ fn type_error<T: ContainerElement>(index: usize) -> 
Result<T> {
     )
 }
 
-/// Mutable, reference-counted sequence of `T`, sharing its underlying
-/// `ListObj` with C++. Cloning is cheap (it bumps the refcount) and yields a
-/// handle to the same list.
+/// A mutable, reference-counted sequence of `T`, shared with C++ and Python
+/// as `ffi.List`.
+///
+/// Cloning a `List` is cheap and yields a handle to the same list, so a change
+/// through one handle is seen through every other. Methods take `&self`.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_ffi::List;
+///
+/// let list = List::<i64>::new();
+/// list.push(1)?;
+/// list.push(2)?;
+/// assert_eq!(list.get(1)?, 2);
+/// assert_eq!(list.len(), 2);
+/// # Ok::<(), tvm_ffi::Error>(())
+/// ```
 #[repr(C)]
 pub struct List<T> {
     data: ObjectArc<ListObj>,
diff --git a/rust/tvm-ffi/src/collections/map.rs 
b/rust/tvm-ffi/src/collections/map.rs
index 5df08c64..a82664fa 100644
--- a/rust/tvm-ffi/src/collections/map.rs
+++ b/rust/tvm-ffi/src/collections/map.rs
@@ -96,8 +96,24 @@ pub struct MapObj {
     pub slots: u64,
 }
 
-/// Immutable, reference-counted map from `K` to `V`, sharing its underlying
-/// `MapObj` with C++. Cloning is cheap (it bumps the refcount).
+/// An immutable, reference-counted map from `K` to `V`, shared with C++ and
+/// Python as `ffi.Map`.
+///
+/// Cloning a `Map` is cheap: it shares the same entries.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_ffi::{Map, String};
+///
+/// let map: Map<String, i64> = [(String::from("a"), 1), (String::from("b"), 
2)]
+///     .into_iter()
+///     .collect();
+/// assert_eq!(map.len(), 2);
+/// assert_eq!(map.get(&String::from("b"))?, Some(2));
+/// assert_eq!(map.get(&String::from("c"))?, None);
+/// # Ok::<(), tvm_ffi::Error>(())
+/// ```
 #[repr(C)]
 pub struct Map<K, V> {
     data: ObjectArc<MapObj>,
diff --git a/rust/tvm-ffi/src/collections/shape.rs 
b/rust/tvm-ffi/src/collections/shape.rs
index 8f305334..fa1a7af4 100644
--- a/rust/tvm-ffi/src/collections/shape.rs
+++ b/rust/tvm-ffi/src/collections/shape.rs
@@ -36,7 +36,18 @@ pub struct ShapeObj {
     data: TVMFFIShapeCell,
 }
 
-/// ABI stable owned Shape for ffi
+/// An immutable sequence of `i64` dimensions, shared with C++ as
+/// `ffi.Shape`.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_ffi::Shape;
+///
+/// let shape = Shape::from([2_i64, 3]);
+/// assert_eq!(shape.as_slice(), &[2, 3]);
+/// assert_eq!(shape.len(), 2);
+/// ```
 #[repr(C)]
 #[derive(ObjectRef, Clone)]
 pub struct Shape {
diff --git a/rust/tvm-ffi/src/collections/tensor.rs 
b/rust/tvm-ffi/src/collections/tensor.rs
index 601cd361..32cbf488 100644
--- a/rust/tvm-ffi/src/collections/tensor.rs
+++ b/rust/tvm-ffi/src/collections/tensor.rs
@@ -87,7 +87,20 @@ pub struct TensorObj {
     dltensor: DLTensor,
 }
 
-/// ABI stable owned Shape for ffi
+/// A reference-counted n-dimensional array, shared with C++ and Python and
+/// exchanged with other frameworks through DLPack.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_ffi::Tensor;
+///
+/// let tensor = Tensor::from_slice(&[1.0_f32, 2.0, 3.0, 4.0, 5.0, 6.0], &[2, 
3])?;
+/// assert_eq!(tensor.shape(), &[2, 3]);
+/// assert_eq!(tensor.numel(), 6);
+/// assert_eq!(tensor.data_as_slice::<f32>()?[4], 5.0);
+/// # Ok::<(), tvm_ffi::Error>(())
+/// ```
 #[repr(C)]
 #[derive(ObjectRef, Clone)]
 pub struct Tensor {
diff --git a/rust/tvm-ffi/src/error.rs b/rust/tvm-ffi/src/error.rs
index 2823ba71..0f2c79be 100644
--- a/rust/tvm-ffi/src/error.rs
+++ b/rust/tvm-ffi/src/error.rs
@@ -25,11 +25,20 @@ use tvm_ffi_sys::{
     TVMFFIErrorMoveFromRaised, TVMFFIErrorSetRaised, TVMFFIObjectHandle, 
TVMFFITypeIndex,
 };
 
-/// Error kind, wraps in a struct to be explicit
+/// The kind of an [`Error`], such as `ValueError`, matching the exception
+/// type that Python raises.
+///
+/// Use the constants such as [`VALUE_ERROR`] and [`TYPE_ERROR`], or
+/// `ErrorKind("MyError")` for a custom kind.
 #[derive(Debug, Clone, PartialEq, Eq)]
 pub struct ErrorKind<'a>(&'a str);
 
 impl<'a> ErrorKind<'a> {
+    /// Get the name of the kind.
+    ///
+    /// # Returns
+    ///
+    /// The name, such as `"ValueError"`.
     pub fn as_str(&self) -> &str {
         self.0
     }
@@ -61,21 +70,69 @@ pub struct ErrorObj {
     cell: TVMFFIErrorCell,
 }
 
-/// Error reference class
+/// An error that crosses the FFI boundary, with a kind, a message and a
+/// backtrace.
+///
+/// An `Error` raised in C++ or Python arrives in Rust as an `Err`, and one
+/// returned from a Rust [`Function`](crate::Function) is raised in the 
caller's
+/// language. Create one with [`bail!`](crate::bail) or
+/// [`ensure!`](crate::ensure), which also record the source location.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_ffi::{bail, Result, VALUE_ERROR};
+///
+/// fn parse_digit(c: char) -> Result<u32> {
+///     match c.to_digit(10) {
+///         Some(digit) => Ok(digit),
+///         None => bail!(VALUE_ERROR, "not a digit: {:?}", c),
+///     }
+/// }
+///
+/// let error = parse_digit('x').unwrap_err();
+/// assert_eq!(error.kind(), VALUE_ERROR);
+/// assert_eq!(error.message(), "not a digit: 'x'");
+/// ```
 #[derive(Clone, ObjectRef)]
 pub struct Error {
     data: ObjectArc<ErrorObj>,
 }
 
-/// Default result that uses Error as the error type
+/// A `Result` whose error type defaults to [`Error`].
 pub type Result<T, E = Error> = std::result::Result<T, E>;
 
 impl Error {
+    /// Create an error.
+    ///
+    /// Prefer [`bail!`](crate::bail), which also records the source location.
+    ///
+    /// # Arguments
+    ///
+    /// * `kind` - The kind of the error, such as [`VALUE_ERROR`].
+    /// * `message` - The message of the error.
+    /// * `traceback` - The backtrace of the error, which may be empty.
+    ///
+    /// # Returns
+    ///
+    /// The error.
     pub fn new(kind: ErrorKind<'_>, message: &str, traceback: &str) -> Self {
         Self::new_with_cause_and_extra_context(kind, message, traceback, None, 
None)
     }
 
     /// Create an error retaining its cause and any application-specific 
context.
+    ///
+    /// # Arguments
+    ///
+    /// * `kind` - The kind of the error.
+    /// * `message` - The message of the error.
+    /// * `traceback` - The backtrace of the error, which may be empty.
+    /// * `cause_chain` - The error that caused this one, if any.
+    /// * `extra_context` - An object of application-specific context, if any.
+    ///
+    /// # Returns
+    ///
+    /// The error.
     pub fn new_with_cause_and_extra_context(
         kind: ErrorKind<'_>,
         message: &str,
@@ -106,10 +163,16 @@ impl Error {
         }
     }
 
-    /// Create a new error by moving from raised error
+    /// Create a new error by moving from the error that the last failed FFI 
call
+    /// raised on this thread.
     ///
     /// # Returns
-    /// The error from the raised error
+    ///
+    /// The raised error.
+    ///
+    /// # Panics
+    ///
+    /// Panics if no error was raised.
     pub fn from_raised() -> Self {
         unsafe {
             let mut error_handle: TVMFFIObjectHandle = std::ptr::null_mut();
@@ -123,41 +186,50 @@ impl Error {
         }
     }
 
-    /// Set the error as raised
+    /// Set the error as raised on this thread, for the caller of an FFI 
function to
+    /// take.
     ///
     /// # Arguments
-    /// * `error` - The error to set as raised
+    ///
+    /// * `error` - The error to set as raised.
     pub fn set_raised(error: &Self) {
         unsafe {
             TVMFFIErrorSetRaised(ObjectArc::as_raw(&error.data) as 
TVMFFIObjectHandle);
         }
     }
 
-    /// Get the kind of the error
+    /// Get the kind of the error.
     ///
     /// # Returns
-    /// The kind of the error
+    ///
+    /// The kind of the error, such as [`VALUE_ERROR`].
     pub fn kind(&self) -> ErrorKind<'_> {
         ErrorKind(&self.data.cell.kind.as_str())
     }
 
-    /// Get the message of the error
+    /// Get the message of the error.
     ///
     /// # Returns
-    /// The message of the error
+    ///
+    /// The message of the error.
     pub fn message(&self) -> &str {
         self.data.cell.message.as_str()
     }
 
-    /// Get the backtrace of the error
+    /// Get the backtrace of the error.
     ///
     /// # Returns
-    /// The backtrace of the error
+    ///
+    /// The backtrace, most recent call first.
     pub fn backtrace(&self) -> &str {
         self.data.cell.backtrace.as_str()
     }
 
     /// Return the cause, if one was attached to this error.
+    ///
+    /// # Returns
+    ///
+    /// The error that caused this one, or `None`.
     pub fn cause_chain(&self) -> Option<Error> {
         let handle = self.data.cell.cause_chain;
         if handle.is_null() {
@@ -173,6 +245,10 @@ impl Error {
     }
 
     /// Return the application-specific context, if present.
+    ///
+    /// # Returns
+    ///
+    /// The context object, or `None`.
     pub fn extra_context(&self) -> Option<object::ObjectRef> {
         let handle = self.data.cell.extra_context;
         if handle.is_null() {
@@ -186,10 +262,12 @@ impl Error {
         }
     }
 
-    /// Get the traceback of the error in the order of most recent call last
+    /// Get the traceback of the error in the order of most recent call last, 
as
+    /// Python prints it.
     ///
     /// # Returns
-    /// The traceback of the error
+    ///
+    /// The traceback of the error.
     pub fn traceback_most_recent_call_last(&self) -> String {
         let backtrace = self.backtrace();
         let backtrace_lines = backtrace.split('\n');
@@ -201,14 +279,20 @@ impl Error {
         traceback
     }
 
-    /// Append the backtrace to the error
+    /// Append the backtrace to the error.
+    ///
+    /// [`attach_context!`](crate::attach_context) appends the current source
+    /// location this way. The error is updated in place when it is not 
shared, and
+    /// copied otherwise.
     ///
     /// # Arguments
-    /// * `this` - The error to append the backtrace to
-    /// * `backtrace` - The backtrace to append
+    ///
+    /// * `this` - The error to append the backtrace to.
+    /// * `backtrace` - The backtrace to append.
     ///
     /// # Returns
-    /// The error with the appended backtrace
+    ///
+    /// The error with the appended backtrace.
     pub fn with_appended_backtrace(this: Self, backtrace: &str) -> Self {
         if ObjectArc::strong_count(&this.data) == 1 {
             // this is the only reference to the error
diff --git a/rust/tvm-ffi/src/extra/dispatch.rs 
b/rust/tvm-ffi/src/extra/dispatch.rs
index cca63484..cb702594 100644
--- a/rust/tvm-ffi/src/extra/dispatch.rs
+++ b/rust/tvm-ffi/src/extra/dispatch.rs
@@ -19,19 +19,23 @@
 
 //! Runtime type dispatch indexed by its tuple of arguments.
 
-/// Runtime type dispatch with a complete handler outcome.
+/// A handler that the structural traversals call with a tuple of arguments.
 ///
-/// Implementations generated by `#[dispatch(prefix, Args, R)]` use a borrowed 
receiver
-/// (`&T` or `&mut T`) as `Self` and use `Ret = Option<R>`. Only the outer
-/// `None` denotes no match: a matched error or inner `None` remains 
`Some(result)`
-/// and does not try another handler. Consuming the receiver reference 
preserves
-/// results borrowing it while ordinary method calls automatically borrow it.
-/// The argument tuple and complete return type select an implementation.
+/// See also [`#[dispatch]`](crate::dispatch).
 #[diagnostic::on_unimplemented(
     message = "this receiver does not implement Dispatcher for the requested 
argument tuple",
     note = "use #[dispatch(prefix, (Carrier, ...), Ret)] with compatible typed 
handlers"
 )]
 pub trait Dispatcher<Args, Ret> {
+    /// Dispatch on `args.0` with the complete argument tuple `args`.
+    ///
+    /// # Arguments
+    ///
+    /// * `args` - The argument tuple, whose first element is the value to 
dispatch on.
+    ///
+    /// # Returns
+    ///
+    /// `Some` of the matched handler's result, or `None` when no handler 
matches.
     fn dispatch_tuple(self, args: Args) -> Ret;
 }
 
diff --git a/rust/tvm-ffi/src/extra/structural_mutate.rs 
b/rust/tvm-ffi/src/extra/structural_mutate.rs
index 99434c65..3b3e4c18 100644
--- a/rust/tvm-ffi/src/extra/structural_mutate.rs
+++ b/rust/tvm-ffi/src/extra/structural_mutate.rs
@@ -342,10 +342,13 @@ impl<T: Into<Any>> IntoMutateResult for Result<T> {
     }
 }
 
-/// Permission to attempt in-place structural mutation along an owned path.
+/// Whether a [`MutationView`] may reuse its value's storage in place.
 ///
-/// `Allow` still requires unique ownership. A [`MutationView`] from an 
exclusive
-/// borrow of an owning `Any` may allow reuse; a shared borrow never grants it.
+/// `Allow` is an upper bound: a value is reused in place only when it is also
+/// uniquely owned. A view made from `&mut Any` starts as `Allow`, and a view
+/// from [`MutationView::borrowed`] is always `Disallow`.
+///
+/// See also [`structural_mutate`].
 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
 pub enum InplaceMode {
     /// Use ordinary mutation, including for uniquely owned values.
@@ -367,20 +370,22 @@ impl InplaceMode {
     }
 }
 
-/// A non-owning, move-only view carrying in-place permission.
+/// A borrowed value that a structural mutation may rewrite in place.
+///
+/// Mutation handlers receive a `MutationView` instead of an owned value, so
+/// matching a node does not retain it. Inspect the node through the view,
+/// then pass the view to [`StructuralMutatorView::default_mutate`] to rewrite
+/// its children, or rewrite one field with [`field`](MutationView::field).
+/// When the node is uniquely owned and the view carries
+/// [`InplaceMode::Allow`], its storage is reused instead of copied.
 ///
-/// This view cannot be copied or cloned. Consuming it transfers the same
-/// permission; narrowing its matched type does not acquire ownership.
+/// `T` is the matched type: `MutationView<'_, Array<Any>>` derefs to the
+/// array's node, and the default `MutationView<'_>` matches every value. The
+/// view is move-only: it cannot be copied or cloned, and consuming it passes
+/// on the same permission. A view made from `&mut Any` may reuse storage;
+/// one made with [`borrowed`](MutationView::borrowed) never does.
 ///
-/// Unlike an owning typed argument, this handle does not increment the 
reference
-/// count. Borrow through it to inspect the node, then consume it in
-/// `default_mutate` to continue recursion. A surviving owning alias
-/// still forces copying. Node borrows come from this handle, not from the
-/// callback context, so the handle can safely be passed to another context.
-/// `T` selects the callback's matched FFI type; `Any` matches every value.
-/// The same handle is passed to consuming [`Dispatcher`] handlers. A publicly
-/// shared-borrow handle always disallows reuse. An exclusive `&mut Any` borrow
-/// grants permission to attempt reuse while keeping its owner borrowed.
+/// See also [`structural_mutate`].
 ///
 /// A node borrowed through a copied view cannot survive consumption of the 
handle:
 /// ```compile_fail,E0505
@@ -448,7 +453,18 @@ impl<'a> MutationView<'a> {
         }
     }
 
-    /// Wrap a borrowed value without granting in-place permission.
+    /// Create a view of a borrowed value that never reuses its storage.
+    ///
+    /// The mutation result is always a new value or [`Unchanged`]. To allow 
reuse,
+    /// make the view from `&mut Any` instead.
+    ///
+    /// # Arguments
+    ///
+    /// * `value` - The value to view.
+    ///
+    /// # Returns
+    ///
+    /// A view with [`InplaceMode::Disallow`].
     #[inline(always)]
     pub fn borrowed(value: AnyView<'a>) -> Self {
         Self::new(value, InplaceMode::Disallow)
@@ -457,6 +473,7 @@ impl<'a> MutationView<'a> {
 
 impl<'a> From<&'a mut Any> for MutationView<'a> {
     /// Borrow an owner exclusively without acquiring a reference count.
+    ///
     /// Reuse still requires the runtime's uniqueness and alias checks.
     #[inline(always)]
     fn from(value: &'a mut Any) -> Self {
@@ -478,13 +495,28 @@ impl<'a, T> MutationView<'a, T> {
         }
     }
 
-    /// Permission carried independently of temporary owning aliases.
+    /// Return the permission this view carries.
+    ///
+    /// # Returns
+    ///
+    /// Whether this view may reuse its value's storage.
     #[inline(always)]
     pub fn inplace_mode(&self) -> InplaceMode {
         self.mode
     }
 
-    /// Restrict permission without upgrading a shared-borrow capability.
+    /// Restrict the permission, without ever upgrading it.
+    ///
+    /// `Disallow` turns off reuse. `Allow` keeps the current mode, so it never
+    /// grants reuse to a view that does not have it, such as a borrowed one.
+    ///
+    /// # Arguments
+    ///
+    /// * `requested` - The permission to restrict to.
+    ///
+    /// # Returns
+    ///
+    /// The view with the restricted permission.
     #[inline(always)]
     pub fn with_inplace_mode(mut self, requested: InplaceMode) -> Self {
         if requested == InplaceMode::Disallow {
@@ -493,8 +525,14 @@ impl<'a, T> MutationView<'a, T> {
         self
     }
 
-    /// Borrow the current value. The view and its copies borrow this handle,
-    /// so their last use must precede consuming it for default mutation.
+    /// Borrow the current value.
+    ///
+    /// The view and its copies borrow this handle, so their last use must 
precede
+    /// consuming it for default mutation.
+    ///
+    /// # Returns
+    ///
+    /// The value as an [`AnyView`].
     #[inline(always)]
     pub fn as_any_view(&self) -> AnyView<'_> {
         // SAFETY: the input owner keeps this payload live while the 
capability is
@@ -503,7 +541,13 @@ impl<'a, T> MutationView<'a, T> {
     }
 
     /// Borrow an object or subtype as `U` without changing ownership or 
permission.
-    /// The reference borrows this capability and cannot survive its 
consumption.
+    ///
+    /// `U` is an object type such as `ArrayObj`. The reference borrows this 
view and
+    /// cannot survive its consumption.
+    ///
+    /// # Returns
+    ///
+    /// The borrowed object, or `None` when the value is not a `U`.
     #[inline(always)]
     pub fn downcast_ref<U: ObjectCore>(&self) -> Option<&U> {
         if self.value.type_index < TVMFFITypeIndex::kTVMFFIStaticObjectBegin 
as i32
@@ -517,6 +561,18 @@ impl<'a, T> MutationView<'a, T> {
     }
 
     /// Narrow the matched type without acquiring an owning reference.
+    ///
+    /// The check is the strict one of [`Any::try_as`], and the view keeps its
+    /// permission.
+    ///
+    /// # Returns
+    ///
+    /// The view typed as `MutationView<'_, U>`.
+    ///
+    /// # Errors
+    ///
+    /// Returns the view unchanged when the value is not a `U`, so another 
type can
+    /// be tried.
     #[inline(always)]
     pub fn try_cast<U: crate::type_traits::ContainerElement>(
         self,
@@ -537,6 +593,10 @@ impl<'a, T> MutationView<'a, T> {
     }
 
     /// Erase the matched type while preserving the carried permission.
+    ///
+    /// # Returns
+    ///
+    /// The view as `MutationView<'_>`.
     #[inline(always)]
     pub fn into_untyped(self) -> MutationView<'a> {
         MutationView {
@@ -574,11 +634,47 @@ impl<T: object::ObjectRefCore> std::ops::Deref for 
MutationView<'_, T> {
 }
 
 impl<T: object::ObjectRefCore> MutationView<'_, T> {
-    /// Borrow a direct owning field while exclusively borrowing this 
capability.
-    /// Permission also requires the parent to remain unique after selection.
-    /// This does not grant writeback to the parent field.
+    /// Borrow a direct owning field of the node as a view of its own, to 
rewrite it
+    /// with [`StructuralMutatorView::mutate`].
+    ///
+    /// The field view exclusively borrows this view. It may reuse the field's
+    /// storage only when this view may and the node remains uniquely owned. 
This
+    /// does not write the result back into the node: keep the node 
[`Unchanged`] or
+    /// rebuild it from the result.
+    ///
+    /// # Arguments
     ///
-    /// Panics if the selector returns storage outside the parent 
representation.
+    /// * `selector` - Returns a reference to one of the node's own owning 
fields,
+    ///   an [`Any`] or a managed reference.
+    ///
+    /// # Returns
+    ///
+    /// A view of the field.
+    ///
+    /// # Panics
+    ///
+    /// Panics if `selector` returns a reference outside the node.
+    ///
+    /// ```
+    /// # use tvm_ffi::object::ObjectRef;
+    /// # use tvm_ffi::*;
+    /// # #[rs_object("my_ext.MyPair", final)]
+    /// # pub struct MyPairObj { base: Object, pub first: Array<i64>, pub 
second: Array<i64> }
+    /// # def_object_ref!(pub MyPair, ObjectRef, MyPairObj);
+    /// # struct Pass;
+    /// # #[dispatch(mutate, (MutationView<'_>, &mut StructuralMutatorView<'_, 
Self>), Result<UnchangedOr<Any>>)]
+    /// # impl Pass {
+    /// #     fn mutate_pair(&mut self, mut pair: MutationView<'_, MyPair>, 
mutator: &mut StructuralMutatorView<'_, Self>) -> Result<UnchangedOr<Any>> {
+    /// let second = mutator.mutate(self, pair.field(|pair| &pair.second))?;
+    /// #         Ok(second)
+    /// #     }
+    /// # }
+    /// # let pair = MyPairObj::new(Object::new(), Array::new(vec![1_i64]), 
Array::new(vec![2_i64]));
+    /// # structural_mutate(MyPair::from(pair), Pass)?;
+    /// # Ok::<(), tvm_ffi::Error>(())
+    /// ```
+    ///
+    /// See also [`structural_mutate`].
     #[inline(always)]
     pub fn field<U: MutationField>(
         &mut self,
@@ -613,8 +709,15 @@ impl<T: object::ObjectRefCore> MutationView<'_, T> {
     }
 }
 
-/// Recursive mutation and default descent for a dispatcher.
-/// Pass the handler's `self` to recursive methods with the same dispatcher 
identity.
+/// The running structural mutation, passed to each handler.
+///
+/// A handler that matches a node decides how its children are rewritten: call
+/// [`default_mutate`](Self::default_mutate) to rewrite every child through the
+/// dispatcher, [`mutate`](Self::mutate) to rewrite one value, or neither to
+/// leave the children as they are. Pass the handler's own `self` as `dispatch`
+/// to every method.
+///
+/// See also [`structural_mutate`].
 pub struct StructuralMutatorView<'a, D, Base = StructuralMutatorObj> {
     runtime: StructuralMutatorHandle,
     _scope: PhantomData<&'a mut ()>,
@@ -624,6 +727,10 @@ pub struct StructuralMutatorView<'a, D, Base = 
StructuralMutatorObj> {
 
 impl<D, Base: StructuralMutatorBase> StructuralMutatorView<'_, D, Base> {
     /// Inspect the base state without retaining the scoped mutator runtime.
+    ///
+    /// # Returns
+    ///
+    /// The base state, borrowed for the lifetime of this view.
     #[inline(always)]
     pub fn base(&self) -> &Base {
         // SAFETY: the unsafe base contract permits shared inspection, 
including
@@ -641,12 +748,33 @@ impl<D, Base: StructuralMutatorBase> 
StructuralMutatorView<'_, D, Base> {
         }
     }
 
+    /// Return the definition region the current value is in.
+    ///
+    /// Reflected fields marked as definitions set it while their values are
+    /// mutated; it is [`DefRegionKind::None`] elsewhere.
+    ///
+    /// # Returns
+    ///
+    /// The current definition region.
     #[inline(always)]
     pub fn def_region_kind(&self) -> DefRegionKind {
         unsafe { std::mem::transmute(*(*self.runtime).def_region_mode.get()) }
     }
 
-    /// Scope definition-region state; an enclosing Pattern keeps precedence.
+    /// Run `callback` under a scoped definition region.
+    ///
+    /// An enclosing [`DefRegionKind::Pattern`] region keeps precedence, since 
a
+    /// pattern applies to everything inside it. The previous region is 
restored
+    /// afterwards.
+    ///
+    /// # Arguments
+    ///
+    /// * `kind` - The definition region to enter.
+    /// * `callback` - Runs inside the region, with this view.
+    ///
+    /// # Returns
+    ///
+    /// The result of `callback`.
     #[inline(always)]
     pub fn with_def_region_kind<R>(
         &mut self,
@@ -658,7 +786,24 @@ impl<D, Base: StructuralMutatorBase> 
StructuralMutatorView<'_, D, Base> {
         })
     }
 
-    /// Re-enter dispatch with the capability's carried permission.
+    /// Mutate `value` through the dispatcher, as if the traversal had reached 
it.
+    ///
+    /// `value` keeps the permission it carries. Use it to rewrite a value 
other
+    /// than the current node, such as a field selected with
+    /// [`MutationView::field`].
+    ///
+    /// # Arguments
+    ///
+    /// * `dispatch` - The handler's own `self`.
+    /// * `value` - The value to mutate.
+    ///
+    /// # Returns
+    ///
+    /// The replacement, or [`Unchanged`].
+    ///
+    /// # Errors
+    ///
+    /// Returns the error of the handler that `value` reaches.
     #[inline(always)]
     pub fn mutate<'input, T>(
         &mut self,
@@ -675,7 +820,27 @@ impl<D, Base: StructuralMutatorBase> 
StructuralMutatorView<'_, D, Base> {
         .and_then(UnchangedOr::from_carrier)
     }
 
-    /// Descend through the native hook or reflected fields without 
redispatching the root.
+    /// Rewrite the children of `value` through the dispatcher, without 
dispatching
+    /// `value` itself again.
+    ///
+    /// Children are found by the type's registered mutation hook or its 
reflected
+    /// fields. The result is unchanged when no child changed; otherwise it is 
a
+    /// node with the new children, made in place when `value` allows it and is
+    /// uniquely owned.
+    ///
+    /// # Arguments
+    ///
+    /// * `dispatch` - The handler's own `self`.
+    /// * `value` - The value whose children to mutate, usually the view the
+    ///   handler received.
+    ///
+    /// # Returns
+    ///
+    /// The rewritten node, or [`Unchanged`].
+    ///
+    /// # Errors
+    ///
+    /// Returns the first error of a child's handler.
     #[inline(always)]
     pub fn default_mutate<'input, T>(
         &mut self,
@@ -696,6 +861,23 @@ impl<D, Base: StructuralMutatorBase> 
StructuralMutatorView<'_, D, Base> {
         .and_then(UnchangedOr::from_carrier)
     }
 
+    /// Look up the replacement recorded for a variable in this mutation.
+    ///
+    /// Default mutation records the result of every free variable and DAG 
node it
+    /// rewrites, and replaces each later occurrence with it.
+    ///
+    /// # Arguments
+    ///
+    /// * `dispatch` - The handler's own `self`.
+    /// * `var` - The variable, an object.
+    ///
+    /// # Returns
+    ///
+    /// The recorded replacement, or `None`.
+    ///
+    /// # Errors
+    ///
+    /// Returns a `TypeError` if `var` is not an object.
     #[inline(always)]
     pub fn var_remap_get(&mut self, dispatch: &mut D, var: AnyView<'_>) -> 
Result<Option<Any>> {
         unsafe {
@@ -707,6 +889,24 @@ impl<D, Base: StructuralMutatorBase> 
StructuralMutatorView<'_, D, Base> {
         }
     }
 
+    /// Record a replacement for a variable for the rest of this mutation.
+    ///
+    /// Default mutation then replaces each later occurrence of `var`, a free
+    /// variable or DAG node, with `value`.
+    ///
+    /// # Arguments
+    ///
+    /// * `dispatch` - The handler's own `self`.
+    /// * `var` - The variable, an object.
+    /// * `value` - The replacement.
+    ///
+    /// # Returns
+    ///
+    /// `Ok(())` once the replacement is recorded.
+    ///
+    /// # Errors
+    ///
+    /// Returns a `TypeError` if `var` is not an object.
     #[inline(always)]
     pub fn var_remap_set(&mut self, dispatch: &mut D, var: AnyView<'_>, value: 
&Any) -> Result<()> {
         unsafe {
@@ -1832,20 +2032,20 @@ impl StructuralMutatorObj {
 ///
 /// # Examples
 ///
-/// Double integer leaves with a typed handler; default mutation visits the 
array:
+/// Double the integers in nested arrays; default mutation descends into the
+/// arrays:
 ///
 /// ```
-/// # pub use tvm_ffi::{extra, match_any, match_any_internal, Dispatcher};
-/// # fn main() -> tvm_ffi::Result<()> {
 /// use tvm_ffi::{
 ///     dispatch, structural_mutate, Any, Array, MutationView, Result, 
StructuralMutatorView,
 ///     UnchangedOr,
 /// };
 ///
 /// struct Double;
+///
 /// #[dispatch(mutate, (MutationView<'_>, &mut StructuralMutatorView<'_, 
Self>), Result<UnchangedOr<Any>>)]
 /// impl Double {
-///     fn mutate_integer(
+///     fn mutate_int(
 ///         &mut self,
 ///         value: i64,
 ///         _: &mut StructuralMutatorView<'_, Self>,
@@ -1854,11 +2054,10 @@ impl StructuralMutatorObj {
 ///     }
 /// }
 ///
-/// let result = structural_mutate(Array::new(vec![1_i64, 2, 3]), Double)?;
-/// let result = Array::<i64>::try_from(result)?;
-/// assert_eq!(result.iter().collect::<Vec<_>>(), vec![2, 4, 6]);
-/// # Ok(())
-/// # }
+/// let root = Array::new(vec![Array::new(vec![1_i64, 2]), 
Array::new(vec![3_i64])]);
+/// let result = Array::<Array<i64>>::try_from(structural_mutate(root, 
Double)?)?;
+/// assert_eq!(result.get(0)?.iter().collect::<Vec<_>>(), vec![2, 4]);
+/// # Ok::<(), tvm_ffi::Error>(())
 /// ```
 pub fn structural_mutate<R, H, M, D, O>(root: R, dispatcher: H) -> Result<Any>
 where
diff --git a/rust/tvm-ffi/src/extra/structural_visit.rs 
b/rust/tvm-ffi/src/extra/structural_visit.rs
index 82d05f4e..a1eed071 100644
--- a/rust/tvm-ffi/src/extra/structural_visit.rs
+++ b/rust/tvm-ffi/src/extra/structural_visit.rs
@@ -287,7 +287,16 @@ crate::def_object_ref! {
 }
 
 impl VisitInterrupt {
-    /// Interrupt carrying an FFI-compatible payload.
+    /// Create an interrupt carrying an FFI-compatible payload back to the 
caller of
+    /// the visit.
+    ///
+    /// # Arguments
+    ///
+    /// * `payload` - The payload, such as the value that was found.
+    ///
+    /// # Returns
+    ///
+    /// The interrupt.
     #[inline(always)]
     pub fn with<T: Into<Any>>(payload: T) -> Self {
         VisitInterruptObj {
@@ -306,9 +315,16 @@ impl VisitInterrupt {
 // continue; VisitInterrupt (including None/Error payloads) and Error stop 
descent.
 // Each ABI ingress/import and egress/export transfers ownership exactly once.
 // Recursive drivers move this carrier unchanged; typed public APIs decode it.
-/// Recursive visiting and default child descent for a dispatcher.
+/// The running structural visit, passed to each handler.
 ///
-/// Pass the handler's `self` to visit methods to recurse with the same state.
+/// A handler that matches a node decides which children are visited: call
+/// [`default_visit`](Self::default_visit) to visit every child,
+/// [`visit`](Self::visit) to visit one value, or neither to skip the children.
+/// Pass the handler's own `self` as `dispatch` to every method. A handler
+/// returns `Ok(None)` to continue, or `Ok(Some(interrupt))` to stop the whole
+/// visit with a [`VisitInterrupt`].
+///
+/// See also [`structural_visit`].
 pub struct StructuralVisitorView<'a, D, Base = StructuralVisitorObj> {
     runtime: StructuralVisitorHandle,
     _scope: PhantomData<(&'a mut (), fn(&mut D) -> Base, Rc<()>)>,
@@ -319,6 +335,10 @@ impl<D, Base: StructuralVisitorBase> 
StructuralVisitorView<'_, D, Base> {
     ///
     /// The reference cannot outlive its view:
     ///
+    /// # Returns
+    ///
+    /// The base state, borrowed for the lifetime of this view.
+    ///
     /// ```compile_fail
     /// use tvm_ffi::{StructuralVisitorObj, StructuralVisitorView};
     /// fn retain<D>(view: &StructuralVisitorView<'_, D>) -> &'static 
StructuralVisitorObj {
@@ -341,14 +361,33 @@ impl<D, Base: StructuralVisitorBase> 
StructuralVisitorView<'_, D, Base> {
         unsafe { &*self.runtime.cast::<Base>() }
     }
 
+    /// Return the definition region the current value is in.
+    ///
+    /// Reflected fields marked as definitions set it while their values are
+    /// visited; it is [`DefRegionKind::None`] elsewhere.
+    ///
+    /// # Returns
+    ///
+    /// The current definition region.
     #[inline(always)]
     pub fn def_region_kind(&self) -> DefRegionKind {
         // SAFETY: engine scopes and trusted ABI hooks store valid enum values.
         unsafe { std::mem::transmute(*(*self.runtime).def_region_mode.get()) }
     }
 
-    /// Run operations under a scoped definition region.
-    /// An enclosing Pattern wins; the previous region is restored on all 
exits.
+    /// Run `callback` under a scoped definition region.
+    ///
+    /// An enclosing [`DefRegionKind::Pattern`] region keeps precedence; the
+    /// previous region is restored on all exits.
+    ///
+    /// # Arguments
+    ///
+    /// * `kind` - The definition region to enter.
+    /// * `callback` - Runs inside the region, with this view.
+    ///
+    /// # Returns
+    ///
+    /// The result of `callback`.
     #[inline(always)]
     pub fn with_def_region_kind<R>(
         &mut self,
@@ -358,7 +397,20 @@ impl<D, Base: StructuralVisitorBase> 
StructuralVisitorView<'_, D, Base> {
         StructuralVisitorObj::with_def_region_kind(self.runtime, kind, || 
callback(self))
     }
 
-    /// Visit a value through the dispatcher.
+    /// Visit `value` through the dispatcher, as if the traversal had reached 
it.
+    ///
+    /// # Arguments
+    ///
+    /// * `dispatch` - The handler's own `self`.
+    /// * `value` - The value to visit.
+    ///
+    /// # Returns
+    ///
+    /// `None` to continue, or the interrupt that stopped the visit.
+    ///
+    /// # Errors
+    ///
+    /// Returns the error of the handler that `value` reaches.
     #[inline(always)]
     pub fn visit<'input, T>(&mut self, dispatch: &mut D, value: T) -> 
Result<Option<VisitInterrupt>>
     where
@@ -378,8 +430,24 @@ impl<D, Base: StructuralVisitorBase> 
StructuralVisitorView<'_, D, Base> {
         }
     }
 
-    /// Run default descent through registered hooks or reflected fields.
-    /// Children re-enter the dispatcher; the current value does not.
+    /// Visit the children of `value` through the dispatcher, without 
dispatching
+    /// `value` itself again.
+    ///
+    /// Children are found by the type's registered visit hook or its reflected
+    /// fields. The visit stops at the first child that returns an interrupt.
+    ///
+    /// # Arguments
+    ///
+    /// * `dispatch` - The handler's own `self`.
+    /// * `value` - The value whose children to visit.
+    ///
+    /// # Returns
+    ///
+    /// `None` to continue, or the interrupt that stopped the visit.
+    ///
+    /// # Errors
+    ///
+    /// Returns the first error of a child's handler.
     #[inline(always)]
     pub fn default_visit<'input, T>(
         &self,
@@ -1148,7 +1216,6 @@ impl<Base: StructuralVisitorBase> 
StructuralVisitEngine<Base> {
 /// Sum integer leaves while default traversal descends into the array:
 ///
 /// ```
-/// # pub use tvm_ffi::{extra, match_any, match_any_internal, Dispatcher};
 /// # fn main() -> tvm_ffi::Result<()> {
 /// use tvm_ffi::{
 ///     dispatch, structural_visit, AnyView, Array, Result, 
StructuralVisitorView,
diff --git a/rust/tvm-ffi/src/extra/unchanged.rs 
b/rust/tvm-ffi/src/extra/unchanged.rs
index e7b9cd4a..94260680 100644
--- a/rust/tvm-ffi/src/extra/unchanged.rs
+++ b/rust/tvm-ffi/src/extra/unchanged.rs
@@ -28,21 +28,51 @@ use crate::error::{Error, Result, TYPE_ERROR};
 use crate::tvm_ffi_sys::{TVMFFIAny, TVMFFITypeIndex};
 use crate::type_traits::{AnyCompatible, ContainerElement};
 
-/// A structural mutation that keeps its input without acquiring another owner.
+/// A marker result that keeps the original value.
 ///
-/// This marker may be returned directly from a map or mutation callback. A
-/// pre-order map still descends into the original value's children.
+/// A map or mutation callback returns `Unchanged`, usually as
+/// `Unchanged.into()`, to keep the value it was given without retaining or
+/// cloning it. A pre-order map still descends into the original value's
+/// children.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_ffi::{Any, Unchanged, UnchangedOr};
+///
+/// let result: UnchangedOr<Any> = Unchanged.into();
+/// assert!(result.is_unchanged());
+/// ```
 #[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
 pub struct Unchanged;
 
-/// A typed replacement or an [`Unchanged`] marker, stored in one FFI Any cell.
+/// Either a replacement value of type `T`, or [`Unchanged`] to keep the
+/// original.
+///
+/// Structural map and mutation callbacks return `Result<UnchangedOr<Any>>`.
+/// It is like `Option<T>` whose `None` means "keep what was there", stored in
+/// one FFI value. `UnchangedOr<Any>` converts with `.into()` from
+/// [`Unchanged`], from a managed reference or scalar, and from a typed
+/// `UnchangedOr<T>`. `T` is an FFI value type or [`Any`]. Converting the
+/// wrapper into `Any` keeps the marker; use [`value_or`](Self::value_or) or
+/// [`value_or_else`](Self::value_or_else) when an actual value is needed.
 ///
-/// `T` may be an FFI value type or [`Any`]. Use `Result<UnchangedOr<T>>` to
-/// propagate errors. Callbacks can return this wrapper, [`Unchanged`], a
-/// replacement value, or a `Result` containing any of these.
+/// # Examples
 ///
-/// Converting this wrapper to `Any` preserves the marker. Use
-/// [`Self::value_or`] or [`Self::value_or_else`] when an actual value is 
needed.
+/// ```
+/// use tvm_ffi::UnchangedOr;
+///
+/// fn double_even(value: i64) -> UnchangedOr<i64> {
+///     if value % 2 == 0 {
+///         UnchangedOr::changed(value * 2)
+///     } else {
+///         UnchangedOr::unchanged()
+///     }
+/// }
+///
+/// assert_eq!(double_even(4).value_or(4), 8);
+/// assert_eq!(double_even(3).value_or(3), 3);
+/// ```
 #[repr(transparent)]
 pub struct UnchangedOr<T: ContainerElement = Any> {
     data: Any,
@@ -50,7 +80,12 @@ pub struct UnchangedOr<T: ContainerElement = Any> {
 }
 
 impl<T: ContainerElement> UnchangedOr<T> {
-    /// Keep the original value without borrowing or cloning it.
+    /// Create a result that keeps the original value, without borrowing or 
cloning
+    /// it.
+    ///
+    /// # Returns
+    ///
+    /// An unchanged result.
     #[inline]
     pub fn unchanged() -> Self {
         Self {
@@ -59,7 +94,15 @@ impl<T: ContainerElement> UnchangedOr<T> {
         }
     }
 
-    /// Supply an owning replacement value.
+    /// Create a result that replaces the original value.
+    ///
+    /// # Arguments
+    ///
+    /// * `value` - The replacement.
+    ///
+    /// # Returns
+    ///
+    /// A result holding `value`.
     #[inline]
     pub fn changed(value: T) -> Self {
         let mut raw = TVMFFIAny::new();
@@ -73,13 +116,36 @@ impl<T: ContainerElement> UnchangedOr<T> {
         }
     }
 
-    /// Whether this result asks its caller to keep the original value.
+    /// Return whether this result keeps the original value.
+    ///
+    /// # Returns
+    ///
+    /// `true` if the result is unchanged.
     #[inline]
     pub fn is_unchanged(&self) -> bool {
         is_unchanged(&self.data)
     }
 
-    /// Whether the result is unchanged or has the original shallow identity.
+    /// Return whether this result is unchanged or holds `original` itself.
+    ///
+    /// An object counts as the same only when it is the same object, not an 
equal
+    /// copy. Use it to tell whether anything was rewritten.
+    ///
+    /// # Arguments
+    ///
+    /// * `original` - The original value.
+    ///
+    /// # Returns
+    ///
+    /// `true` if the result is unchanged or holds `original`.
+    ///
+    /// ```
+    /// use tvm_ffi::{Array, UnchangedOr};
+    /// let original = Array::new(vec![1_i64]);
+    /// 
assert!(UnchangedOr::changed(original.clone()).unchanged_or_same_as(&original));
+    /// ```
+    ///
+    /// See also [`UnchangedOr`].
     #[inline]
     pub fn unchanged_or_same_as(&self, original: &T) -> bool {
         if self.is_unchanged() {
@@ -93,7 +159,11 @@ impl<T: ContainerElement> UnchangedOr<T> {
         raw_ffi_any_same_as(raw, *self.data.as_raw_ffi_any())
     }
 
-    /// Move out the replacement, returning None when unchanged.
+    /// Move out the replacement.
+    ///
+    /// # Returns
+    ///
+    /// The replacement, or `None` when the result is unchanged.
     #[inline]
     pub fn into_option(self) -> Option<T> {
         if self.is_unchanged() {
@@ -106,19 +176,47 @@ impl<T: ContainerElement> UnchangedOr<T> {
         }
     }
 
-    /// Move the replacement or the supplied original value out of this result.
+    /// Move out the replacement, or `original` when the result is unchanged.
+    ///
+    /// `original` is taken even when it is not returned; use
+    /// [`value_or_else`](Self::value_or_else) to produce it only when needed.
+    ///
+    /// # Arguments
+    ///
+    /// * `original` - The original value.
+    ///
+    /// # Returns
+    ///
+    /// The replacement, or `original`.
     #[inline]
     pub fn value_or(self, original: T) -> T {
         self.value_or_else(|| original)
     }
 
-    /// Materialize the original only if this result is unchanged.
+    /// Move out the replacement, or materialize the original only if this 
result is
+    /// unchanged.
+    ///
+    /// # Arguments
+    ///
+    /// * `original` - Produces the original value; called only when unchanged.
+    ///
+    /// # Returns
+    ///
+    /// The replacement, or the result of `original`.
     #[inline]
     pub fn value_or_else(self, original: impl FnOnce() -> T) -> T {
         self.into_option().unwrap_or_else(original)
     }
 
-    /// Convert only the replacement, preserving an unchanged marker.
+    /// Convert only the replacement, preserving an unchanged result.
+    ///
+    /// # Arguments
+    ///
+    /// * `convert` - Converts the replacement; called only when there is one.
+    ///
+    /// # Returns
+    ///
+    /// The converted result.
     #[inline]
     pub fn map<U: ContainerElement>(self, convert: impl FnOnce(T) -> U) -> 
UnchangedOr<U> {
         match self.into_option() {
@@ -127,7 +225,27 @@ impl<T: ContainerElement> UnchangedOr<T> {
         }
     }
 
-    /// Fallibly convert only the replacement, propagating its error unchanged.
+    /// Fallibly convert only the replacement, preserving an unchanged result.
+    ///
+    /// # Arguments
+    ///
+    /// * `convert` - Converts the replacement; called only when there is one.
+    ///
+    /// # Returns
+    ///
+    /// The converted result.
+    ///
+    /// # Errors
+    ///
+    /// Returns the error of `convert` unchanged.
+    ///
+    /// ```
+    /// use tvm_ffi::UnchangedOr;
+    /// let result = 
UnchangedOr::changed(3_i64).try_map(u32::try_from).unwrap();
+    /// assert_eq!(result.into_option(), Some(3_u32));
+    /// ```
+    ///
+    /// See also [`UnchangedOr`].
     #[inline]
     pub fn try_map<U: ContainerElement, E>(
         self,
@@ -141,9 +259,26 @@ impl<T: ContainerElement> UnchangedOr<T> {
 
     /// Check a replacement's FFI type without cloning it.
     ///
-    /// An unchanged result is compatible with every replacement type. Erased
-    /// and narrowing conversions retain the strict type check used by
-    /// [`Any::try_as`]. For a known typed conversion, use `map(Into::into)`.
+    /// An unchanged result is compatible with every replacement type. Erased 
and
+    /// narrowing conversions retain the strict type check of [`Any::try_as`]. 
For a
+    /// known typed conversion, use `map(Into::into)`.
+    ///
+    /// # Returns
+    ///
+    /// The result typed as `UnchangedOr<U>`.
+    ///
+    /// # Errors
+    ///
+    /// Returns a `TypeError` if the replacement is not stored as `U`.
+    ///
+    /// ```
+    /// use tvm_ffi::{Any, UnchangedOr};
+    /// let erased: UnchangedOr<Any> = 7_i64.into();
+    /// assert_eq!(erased.try_cast::<i64>()?.into_option(), Some(7));
+    /// # Ok::<(), tvm_ffi::Error>(())
+    /// ```
+    ///
+    /// See also [`UnchangedOr`].
     #[inline]
     pub fn try_cast<U: ContainerElement>(self) -> Result<UnchangedOr<U>> {
         if unsafe { 
UnchangedOr::<U>::check_any_strict(self.data.as_raw_ffi_any()) } {
diff --git a/rust/tvm-ffi/src/function.rs b/rust/tvm-ffi/src/function.rs
index f375ba7e..4093b113 100644
--- a/rust/tvm-ffi/src/function.rs
+++ b/rust/tvm-ffi/src/function.rs
@@ -38,7 +38,24 @@ pub struct FunctionObj {
     cell: TVMFFIFunctionCell,
 }
 
-/// Error reference class
+/// A type-erased callable that works across languages.
+///
+/// A `Function` takes [`AnyView`] arguments and returns an [`Any`]. Make one
+/// from a Rust closure with [`from_typed`](Self::from_typed), look one up by
+/// name with [`get_global`](Self::get_global), and call it with
+/// [`call_tuple`](Self::call_tuple) or [`call_packed`](Self::call_packed).
+/// [`into_typed_fn!`](crate::into_typed_fn) turns it back into a typed 
closure.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_ffi::{Function, Result};
+///
+/// let add = Function::from_typed(|x: i64, y: i64| -> Result<i64> { Ok(x + y) 
});
+/// let sum = i64::try_from(add.call_tuple((1_i64, 2_i64))?)?;
+/// assert_eq!(sum, 3);
+/// # Ok::<(), tvm_ffi::Error>(())
+/// ```
 #[derive(Clone, ObjectRef)]
 pub struct Function {
     data: ObjectArc<FunctionObj>,
@@ -112,7 +129,29 @@ unsafe impl<F: Fn(&[AnyView]) -> Result<Any> + 'static> 
ObjectCore for CallbackF
 }
 
 impl Function {
-    /// Call the function in packed format.
+    /// Call the function with the arguments `packed_args`.
+    ///
+    /// # Arguments
+    ///
+    /// * `packed_args` - The arguments, one [`AnyView`] each.
+    ///
+    /// # Returns
+    ///
+    /// The function's result.
+    ///
+    /// # Errors
+    ///
+    /// Returns the error that the function raises.
+    ///
+    /// ```
+    /// use tvm_ffi::{AnyView, Function, Result};
+    /// let add = Function::from_typed(|x: i64, y: i64| -> Result<i64> { Ok(x 
+ y) });
+    /// let result = add.call_packed(&[AnyView::from(&1_i64), 
AnyView::from(&2_i64)])?;
+    /// assert_eq!(result.try_as::<i64>(), Some(3));
+    /// # Ok::<(), tvm_ffi::Error>(())
+    /// ```
+    ///
+    /// See also [`Function`].
     pub fn call_packed(&self, packed_args: &[AnyView]) -> Result<Any> {
         unsafe {
             let packed_args_ptr = packed_args.as_ptr() as *const TVMFFIAny;
@@ -131,6 +170,32 @@ impl Function {
         }
     }
 
+    /// Call the function with the arguments in the tuple `tuple_args`.
+    ///
+    /// An element may be a value or a reference to one; a reference passes an
+    /// object without retaining it.
+    ///
+    /// # Arguments
+    ///
+    /// * `tuple_args` - The arguments, as a tuple such as `(1_i64, &array)`.
+    ///
+    /// # Returns
+    ///
+    /// The function's result.
+    ///
+    /// # Errors
+    ///
+    /// Returns the error that the function raises.
+    ///
+    /// ```
+    /// use tvm_ffi::{Function, Result};
+    /// let add = Function::from_typed(|x: i64, y: i64| -> Result<i64> { Ok(x 
+ y) });
+    /// let result = add.call_tuple((1_i64, 2_i64))?;
+    /// assert_eq!(result.try_as::<i64>(), Some(3));
+    /// # Ok::<(), tvm_ffi::Error>(())
+    /// ```
+    ///
+    /// See also [`Function`].
     pub fn call_tuple<TupleType>(&self, tuple_args: TupleType) -> Result<Any>
     where
         TupleType: TupleAsPackedArgs,
@@ -161,15 +226,22 @@ impl Function {
         (&tuple_args).fill_any_view(packed_args);
         self.call_packed(packed_args)
     }
-    /// Call function with compile-time known argument count
-    /// This is an optimized version of call_tuple for when the argument count
-    /// is known at compile time, avoiding the small vector optimization 
overhead.
+    /// Call the function with the `LEN` arguments in `tuple_args`.
+    ///
+    /// This is [`call_tuple`](Self::call_tuple) for an argument count known at
+    /// compile time, without its small-vector overhead.
     ///
     /// # Arguments
-    /// * `tuple_args` - The tuple arguments
+    ///
+    /// * `tuple_args` - The `LEN` arguments, as a tuple.
     ///
     /// # Returns
-    /// * `Any` - The result
+    ///
+    /// The function's result.
+    ///
+    /// # Errors
+    ///
+    /// Returns the error that the function raises.
     pub fn call_tuple_with_len<const LEN: usize, TupleType>(
         &self,
         tuple_args: TupleType,
@@ -181,14 +253,30 @@ impl Function {
         (&tuple_args).fill_any_view(&mut packed_args);
         self.call_packed(&packed_args)
     }
-    /// Get global function by name
-    /// This function will throw an error if the function is not found.
+    /// Look up the global function registered as `name`.
     ///
     /// # Arguments
-    /// * `name` - The name of the function
+    ///
+    /// * `name` - The name of the function, such as `"my_ext.double"`.
     ///
     /// # Returns
-    /// * `Function` - The global function
+    ///
+    /// The global function.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if no function is registered as `name`.
+    ///
+    /// ```
+    /// use tvm_ffi::Function;
+    /// let double = Function::from_typed(|x: i64| -> tvm_ffi::Result<i64> { 
Ok(x * 2) });
+    /// Function::register_global("my_ext.double", double)?;
+    /// let double = Function::get_global("my_ext.double")?;
+    /// assert_eq!(double.call_tuple((21_i64,))?.try_as::<i64>(), Some(42));
+    /// # Ok::<(), tvm_ffi::Error>(())
+    /// ```
+    ///
+    /// See also [`register_global_typed`](Self::register_global_typed).
     pub fn get_global(name: &str) -> Result<Function> {
         unsafe {
             let name_arg = TVMFFIByteArray::from_str(name);
@@ -203,24 +291,30 @@ impl Function {
         }
     }
 
-    /// Look up a reflected method of a type by type index and method name
+    /// Look up a reflected method of a type by type index and method name.
     ///
     /// Methods registered through the C++ reflection registry
-    /// (`refl::ObjectDef<T>().def(...)`) live in the per-type method table
-    /// rather than the global function table. Constructors registered via
-    /// `refl::init` are reachable under the reserved name `__ffi_init__`.
-    /// For instance methods, the first packed argument is the object itself.
+    /// (`refl::ObjectDef<T>().def(...)`) live in the per-type method table 
rather
+    /// than the global function table. Constructors registered via 
`refl::init` are
+    /// reachable under the reserved name `__ffi_init__`. For instance 
methods, the
+    /// first argument is the object itself.
     ///
-    /// `type_index` must be a registered type index (e.g. obtained from a
-    /// live object via `Any::type_index` or from a type key); the underlying
-    /// C API treats an unregistered index as a fatal error.
+    /// `type_index` must be a registered type index, obtained for example 
from a
+    /// live object via `Any::type_index` or from a type key; the C API treats 
an
+    /// unregistered index as a fatal error.
     ///
     /// # Arguments
-    /// * `type_index` - The type index of the type that owns the method
-    /// * `method_name` - The name of the method
+    ///
+    /// * `type_index` - The type index of the type that owns the method.
+    /// * `method_name` - The name of the method.
     ///
     /// # Returns
-    /// * `Function` - The reflected method
+    ///
+    /// The reflected method.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if the type has no method `method_name`.
     pub fn from_type_method(type_index: i32, method_name: &str) -> 
Result<Function> {
         unsafe {
             let type_info = TVMFFIGetTypeInfo(type_index);
@@ -260,9 +354,22 @@ impl Function {
         }
     }
 
-    /// Look up a function-valued attribute for a concrete runtime type.
+    /// Look up a function-valued attribute of a concrete runtime type.
     ///
     /// Type attributes are not inherited from base types.
+    ///
+    /// # Arguments
+    ///
+    /// * `type_index` - The type index of the type.
+    /// * `attr_name` - The name of the attribute.
+    ///
+    /// # Returns
+    ///
+    /// The function stored in the attribute.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if the type has no such attribute, or it is not a 
function.
     pub fn from_type_attr(type_index: i32, attr_name: &str) -> 
Result<Function> {
         let value = crate::reflection::get_type_attr(type_index, 
attr_name).ok_or_else(|| {
             crate::error::Error::new(
@@ -277,17 +384,23 @@ impl Function {
         Function::try_from(value)
     }
 
-    /// Look up a reflected method of a type by type key and method name
+    /// Look up a reflected method of a type by type key and method name.
     ///
-    /// Same as [`Function::from_type_method`], but resolves `type_key` to a
-    /// type index first.
+    /// Same as [`Function::from_type_method`], but resolves `type_key` to a 
type
+    /// index first.
     ///
     /// # Arguments
-    /// * `type_key` - The type key of the type that owns the method
-    /// * `method_name` - The name of the method
+    ///
+    /// * `type_key` - The type key of the type that owns the method.
+    /// * `method_name` - The name of the method.
     ///
     /// # Returns
-    /// * `Function` - The reflected method
+    ///
+    /// The reflected method.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if the type or the method does not exist.
     pub fn from_type_key_method(type_key: &str, method_name: &str) -> 
Result<Function> {
         unsafe {
             let type_key_arg = TVMFFIByteArray::from_str(type_key);
@@ -297,37 +410,61 @@ impl Function {
         }
     }
 
-    /// Register a function as a global function
+    /// Register a function as a global function, which every language can then
+    /// look up by name.
     ///
-    /// The function's metadata records the type schema of an untyped
-    /// function, as C++ `refl::GlobalDef().def_packed` does. Use
-    /// [`Function::register_global_typed`] to record the parameter and return
-    /// types of a typed function, and a doc string.
+    /// The function's metadata records the type schema of an untyped 
function, as
+    /// C++ `refl::GlobalDef().def_packed` does. Use
+    /// [`Function::register_global_typed`] to record the parameter and return 
types
+    /// of a typed function, and a doc string.
     ///
     /// # Arguments
-    /// * `name` - The name of the function
-    /// * `func` - The function to register
+    ///
+    /// * `name` - The name of the function.
+    /// * `func` - The function to register.
     ///
     /// # Returns
-    /// * `Result<()>` - The result of the registration
+    ///
+    /// `Ok(())` once the function is registered.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if the registration fails.
+    ///
+    /// See also [`get_global`](Self::get_global).
     pub fn register_global(name: &str, func: Function) -> Result<()> {
         let type_schema = 
crate::type_traits::type_schema(FunctionObj::TYPE_KEY, &[]);
         Self::register_global_with_info(name, &func, &type_schema, "")
     }
 
-    /// Register a typed function as a global function, with a doc string
+    /// Register a typed function as a global function, with a doc string.
     ///
-    /// As C++ `refl::GlobalDef().def` does, the function's metadata records
-    /// its type schema, which `tvm_ffi.get_global_func_metadata` and the stub
-    /// generator read. An empty `doc` records no doc string.
+    /// As C++ `refl::GlobalDef().def` does, the function's metadata records 
its
+    /// type schema, which `tvm_ffi.get_global_func_metadata` and the stub 
generator
+    /// read. An empty `doc` records no doc string.
     ///
     /// # Arguments
-    /// * `name` - The name of the function
-    /// * `func` - The typed function to register, as for 
[`Function::from_typed`]
-    /// * `doc` - The doc string of the function
+    ///
+    /// * `name` - The name of the function.
+    /// * `func` - The typed function to register, as for 
[`Function::from_typed`].
+    /// * `doc` - The doc string of the function.
     ///
     /// # Returns
-    /// * `Result<()>` - The result of the registration
+    ///
+    /// `Ok(())` once the function is registered.
+    ///
+    /// # Errors
+    ///
+    /// Returns an error if the registration fails.
+    ///
+    /// ```
+    /// use tvm_ffi::{Function, Result};
+    /// let add = |x: i64, y: i64| -> Result<i64> { Ok(x + y) };
+    /// Function::register_global_typed("my_ext.add", add, "Add two 
integers.")?;
+    /// # Ok::<(), tvm_ffi::Error>(())
+    /// ```
+    ///
+    /// See also [`get_global`](Self::get_global).
     pub fn register_global_typed<F, I, O>(name: &str, func: F, doc: &str) -> 
Result<()>
     where
         F: AsPackedCallable<I, O> + 'static,
@@ -357,15 +494,32 @@ impl Function {
             
crate::check_safe_call!(TVMFFIFunctionSetGlobalFromMethodInfo(&info, 
can_override))
         }
     }
-    /// Construct a function from a packed function
+    /// Construct a function from a packed function, which takes its arguments 
as a
+    /// slice.
+    ///
+    /// Use it for a variable number of arguments; otherwise
+    /// [`from_typed`](Self::from_typed) converts the arguments for you. Report
+    /// errors by returning them. A panic in `func` is caught and raised to the
+    /// caller as an `InternalError`, but panicking is discouraged.
+    ///
     /// # Arguments
-    /// * `func` - The packed function in signature of `Fn(&[AnyView]) -> 
Result<Any>`
+    ///
+    /// * `func` - The packed function, of signature `Fn(&[AnyView]) -> 
Result<Any>`.
     ///
     /// # Returns
-    /// * `Function` - The function
     ///
-    /// Report errors by returning them. A panic in `func` is caught and raised
-    /// to the caller as an `InternalError`, but panicking is discouraged.
+    /// The function.
+    ///
+    /// ```
+    /// use tvm_ffi::{Any, AnyView, Function, Result};
+    /// let count = Function::from_packed(|args: &[AnyView]| -> Result<Any> {
+    ///     Ok(Any::from(args.len() as i64))
+    /// });
+    /// assert_eq!(count.call_tuple((1_i64, true))?.try_as::<i64>(), Some(2));
+    /// # Ok::<(), tvm_ffi::Error>(())
+    /// ```
+    ///
+    /// See also [`Function`].
     pub fn from_packed<F>(func: F) -> Self
     where
         F: Fn(&[AnyView]) -> Result<Any> + 'static,
@@ -379,15 +533,29 @@ impl Function {
         }
     }
 
-    /// Construct a function from a typed function
+    /// Construct a function from a typed function.
+    ///
+    /// Each argument is converted to its parameter type, and a mismatch 
raises a
+    /// `TypeError` to the caller. Report errors by returning them. A panic in
+    /// `func` is caught and raised to the caller as an `InternalError`, but
+    /// panicking is discouraged.
+    ///
     /// # Arguments
-    /// * `func` - The typed function with function signature of `F(T0, T1, 
...) -> Result<O>`
+    ///
+    /// * `func` - The typed function, of signature `Fn(T0, T1, ...) -> 
Result<O>`.
     ///
     /// # Returns
-    /// * `Function` - The function
     ///
-    /// Report errors by returning them. A panic in `func` is caught and raised
-    /// to the caller as an `InternalError`, but panicking is discouraged.
+    /// The function.
+    ///
+    /// ```
+    /// use tvm_ffi::{Function, Result};
+    /// let scale = Function::from_typed(|x: f64, factor: f64| -> Result<f64> 
{ Ok(x * factor) });
+    /// assert_eq!(scale.call_tuple((2.0_f64, 1.5_f64))?.try_as::<f64>(), 
Some(3.0));
+    /// # Ok::<(), tvm_ffi::Error>(())
+    /// ```
+    ///
+    /// See also [`Function`].
     pub fn from_typed<F, I, O>(func: F) -> Self
     where
         F: AsPackedCallable<I, O> + 'static,
diff --git a/rust/tvm-ffi/src/lib.rs b/rust/tvm-ffi/src/lib.rs
index 6f022fdd..a8d3544f 100644
--- a/rust/tvm-ffi/src/lib.rs
+++ b/rust/tvm-ffi/src/lib.rs
@@ -16,6 +16,9 @@
  * specific language governing permissions and limitations
  * under the License.
  */
+// Lets macro-generated `::tvm_ffi` paths resolve inside this crate too.
+extern crate self as tvm_ffi;
+
 pub mod any;
 pub mod big_int;
 pub mod collections;
@@ -79,7 +82,223 @@ pub use crate::rvalue_ref::RValueRef;
 pub use crate::static_init::run_static_init_blocks;
 pub use crate::string::{Bytes, String};
 pub use crate::type_traits::AnyCompatible;
-pub use tvm_ffi_macros::{def_object_ref, dispatch, match_any, rs_object};
+/// Generate runtime type dispatch from the `prefix_*` methods of an impl.
+///
+/// `#[dispatch(prefix, (First, Args...), Ret)]` on an inherent impl makes its
+/// `prefix_*` methods handlers and adds a `dispatch(first, args...) ->
+/// Option<Ret>` method. `dispatch` calls the first handler, in source order,
+/// whose first parameter matches the runtime type of `first`, and returns
+/// `None` when no handler matches. The macro also implements [`Dispatcher`]
+/// for `&Self` or `&mut Self`, so the type can drive [`structural_walk`] and
+/// the other traversals, whose engines call it and run their own default
+/// recursion; no prefix has a reserved meaning.
+///
+/// # Arguments
+///
+/// * `prefix` - The handler name prefix; `describe` selects `describe_int`,
+///   `describe_str` and so on.
+/// * `(First, Args...)` - The complete input tuple. `First`, the value to
+///   match, is `AnyView<'_>` or [`MutationView<'_>`], including aliases.
+///   `Args` are passed through: each handler takes a leading prefix of them
+///   without cloning, and need not take all of them.
+/// * `Ret` - The result type. A handler's result is converted with
+///   `Into<Ret>`. When both the handler's result and `Ret` are written
+///   `Result<..>`, the success value is converted instead, so a
+///   `Result<UnchangedOr<Expr>>` handler fits a `Result<UnchangedOr<Any>>`
+///   dispatcher.
+///
+/// # Handlers
+///
+/// A handler's first parameter selects what it matches, as for
+/// [`match_any!`]: an owned FFI value such as `i64` or `Array<Any>`, converted
+/// strictly; `&NodeObj`, which borrows the object node; `MutationView<'_, T>`,
+/// which keeps the permission to mutate in place; or `AnyView<'_>`, which
+/// matches every value and must be the last handler.
+///
+/// Only the outer `None` means that no handler matched: a handler error, or a
+/// `None` inside `Ret`, is final. Handlers share one receiver, `&self` or
+/// `&mut self`, and the same method generics and bounds; shared generics must
+/// occur in the receiver, the input tuple or `Ret`. Use the same lifetime name
+/// in an input and `Ret` to return a borrow of that input; elided output
+/// lifetimes borrow the receiver. Handler-level `cfg` attributes are not
+/// supported; configure the whole impl instead. Generate one dispatcher per
+/// type; when inference is ambiguous, give the expected result type.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_ffi::{dispatch, AnyView};
+///
+/// struct Describer;
+///
+/// #[dispatch(describe, (AnyView<'_>,), String)]
+/// impl Describer {
+///     fn describe_int(&self, value: i64) -> String {
+///         format!("integer {value}")
+///     }
+///
+///     fn describe_str(&self, value: tvm_ffi::String) -> String {
+///         format!("string {}", value.as_str())
+///     }
+/// }
+///
+/// let text = tvm_ffi::String::from("hi");
+/// assert_eq!(Describer.dispatch(AnyView::from(&1_i64)).as_deref(), 
Some("integer 1"));
+/// assert_eq!(Describer.dispatch(AnyView::from(&text)).as_deref(), 
Some("string hi"));
+/// assert_eq!(Describer.dispatch(AnyView::from(&1.5_f64)), None);
+/// ```
+///
+/// [`MutationView<'_>`]: crate::MutationView
+/// [`Dispatcher`]: crate::Dispatcher
+/// [`structural_walk`]: crate::structural_walk
+pub use tvm_ffi_macros::dispatch;
+
+/// Match the runtime type of a value, the way `match` matches an enum.
+///
+/// The value is an [`Any`], an [`AnyView`], a managed reference or a
+/// [`MutationView`]. It is evaluated once and taken by value; write `&value`
+/// to keep it. Arms are tried in order, and the first arm whose type matches
+/// runs; a guard that is false moves on to the next arm. The last arm must be
+/// an unguarded `_ => ...`, since new types can be registered at any time.
+///
+/// * `Type(name)` binds an owned value of `Type`, such as `i64` or
+///   `Array<Any>`.
+/// * `Type(ref name)` borrows a managed reference, and `NodeObj(ref name)`
+///   borrows the object node, without changing its reference count.
+/// * `MutationView<Type>(name)` moves the permission to mutate in place into a
+///   typed view.
+///
+/// Matching is strict: an `i64` arm does not match a stored `f64`, and a value
+/// is never truncated. Type aliases and qualified paths may name `Type`. Typed
+/// `MutationView` arms do not match a `Map`, including a nested one; use
+/// `MutationView<Any>` for those. Ordinary `Map<K, V>` arms do match.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_ffi::{match_any, Any, Array};
+///
+/// fn kind(value: &Any) -> String {
+///     match_any! {
+///         value {
+///             i64(n) => format!("integer {n}"),
+///             tvm_ffi::String(s) => format!("string {}", s.as_str()),
+///             Array<Any>(arr) => format!("array of {} items", arr.len()),
+///             _ => "something else".to_string(),
+///         }
+///     }
+/// }
+///
+/// assert_eq!(kind(&Any::from(42_i64)), "integer 42");
+/// assert_eq!(kind(&Any::from(Array::new(vec![1_i64, 2, 3]))), "array of 3 
items");
+/// assert_eq!(kind(&Any::from(1.5_f64)), "something else");
+/// ```
+pub use tvm_ffi_macros::match_any;
+
+/// Define an object type in Rust, as Python's `py_class` does.
+///
+/// `#[rs_object("type.Key", options...)]` registers the struct as an object
+/// type with the reflection of every field, so that C++ structural equality
+/// and hashing, `repr`, deep copy and Python see the fields. The first field,
+/// `base`, is the parent's object, as the base class of a C++ object is:
+/// `base: Object` for a type under `ffi.Object`. Define the managed reference
+/// with [`def_object_ref!`].
+///
+/// The macro adds to the struct:
+///
+/// * `#[repr(C)]` and `Deref` to the parent's object, so `obj.field` reads the
+///   fields of both.
+/// * `MyPairObj::new(base, fields...)`, which takes the fields in order: a
+///   scalar (`bool`, an integer or a float) as its own type, so that a literal
+///   infers as it, and any other field from anything that converts into it.
+///
+/// # Arguments
+///
+/// * `"type.Key"` - The type key.
+/// * `final` - The type has no subtypes.
+/// * `s_eq_hash_kind = "TreeNode"` - The [`SEqHashKind`] of structural
+///   equality; by default the parent's.
+/// * `child_slots = N`, `child_slots_can_overflow = bool` - Type indices kept
+///   for subtypes; 0 and `true` by default.
+/// * `reflection = path` - A `fn(&mut ObjectDef<Self>)` that registers the
+///   type's methods.
+/// * `crate = path` - Where the generated code finds tvm-ffi, for a crate that
+///   depends on it only through a re-export; by default `tvm_ffi`.
+///
+/// A field takes `#[reflect(writable, seq_hash_ignore, ...)]` for the
+/// [`FieldFlags`] of those names, and `name = "..."` or `doc = "..."` to
+/// register it under another name or with a doc string, by default its `///`
+/// comment.
+///
+/// # Requirements
+///
+/// A field's type must implement `AnyCompatible` and `Clone`; an `init_off`
+/// field's type must take all-zero bytes as a value. The parent's object must
+/// implement `ShallowCopy` and `UnsafeInit`, as `rs_object` types and 
non-final
+/// C++ types bound by `tvm-ffi-stubgen` do. The type registers on its first
+/// `type_index()`, with its fields, methods, `__ffi_new__` and, unless the
+/// methods define it, `__ffi_init__`.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_ffi::object::ObjectRef;
+/// use tvm_ffi::{def_object_ref, rs_object, Object};
+///
+/// #[rs_object("my_ext.MyPair", final)]
+/// pub struct MyPairObj {
+///     base: Object,
+///     pub first: i64,
+///     #[reflect(seq_hash_ignore)]
+///     pub second: tvm_ffi::String,
+/// }
+/// def_object_ref!(pub MyPair, ObjectRef, MyPairObj);
+///
+/// let pair = MyPair::from(MyPairObj::new(Object::new(), 1, "one"));
+/// assert_eq!(pair.first, 1);
+/// assert_eq!(pair.second.as_str(), "one");
+/// ```
+pub use tvm_ffi_macros::rs_object;
+
+/// Define the managed reference to an object type, as C++
+/// `TVM_FFI_DEFINE_OBJECT_REF_METHODS_NOTNULLABLE` does.
+///
+/// `def_object_ref!(TypeName, ParentType, ObjectName)` defines `TypeName`, a
+/// `derive(ObjectRef)` type that holds an `ObjectName`. It dereferences to
+/// `ObjectName`, and an `ObjectName` converts into it.
+///
+/// # Arguments
+///
+/// * `TypeName` - The reference type to define. Attributes and visibility
+///   before it go on the struct.
+/// * `ParentType` - The reference to the parent's object, `ObjectRef` for a
+///   type under `ffi.Object`. `TypeName` converts into it by
+///   `impl_object_upcast!`; a conversion into a further ancestor takes its own
+///   `impl_object_upcast!`.
+/// * `ObjectName` - The object type.
+/// * `crate = path` - Optional, after `ObjectName`: where the generated code
+///   finds tvm-ffi, as for [`rs_object`](macro@rs_object).
+///
+/// # Examples
+///
+/// ```
+/// use tvm_ffi::object::ObjectRef;
+/// use tvm_ffi::{def_object_ref, rs_object, Object};
+///
+/// #[rs_object("my_ext.MyPair", final)]
+/// pub struct MyPairObj {
+///     base: Object,
+///     pub first: i64,
+///     pub second: i64,
+/// }
+///
+/// def_object_ref!(pub MyPair, ObjectRef, MyPairObj);
+///
+/// let pair = MyPair::from(MyPairObj::new(Object::new(), 1, 2));
+/// assert_eq!(pair.first + pair.second, 3);
+/// let _parent: ObjectRef = pair.into();
+/// ```
+pub use tvm_ffi_macros::def_object_ref;
 
 pub use tvm_ffi_sys::TVMFFITypeIndex as TypeIndex;
 pub use tvm_ffi_sys::{
diff --git a/rust/tvm-ffi/src/macros.rs b/rust/tvm-ffi/src/macros.rs
index d53f4b0c..36bbe615 100644
--- a/rust/tvm-ffi/src/macros.rs
+++ b/rust/tvm-ffi/src/macros.rs
@@ -128,17 +128,38 @@ macro_rules! check_safe_call {
     }};
 }
 
-/// Create a new error with file/line info attached
+/// Return an [`Error`](crate::Error) of a kind with a formatted message from
+/// the current function.
 ///
-/// This macro automatically appends file/line info to the traceback
+/// `bail!(kind, fmt, args...)` formats the message as `format!` does and
+/// records the source location in the backtrace.
 ///
 /// # Arguments
-/// * `error_kind` - The kind of the error
-/// * `msg` - The message of the error
-/// * `args` - The posisble format arguments
+///
+/// * `kind` - The kind of the error, such as 
[`VALUE_ERROR`](crate::VALUE_ERROR).
+/// * `fmt` - The format string of the message.
+/// * `args` - The format arguments.
 ///
 /// # Returns
-/// * `Result<(), Error>` - The result of the safe call
+///
+/// Returns `Err` of the error from the enclosing function.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_ffi::{bail, Result, INDEX_ERROR};
+///
+/// fn get(items: &[i64], index: usize) -> Result<i64> {
+///     if index >= items.len() {
+///         bail!(INDEX_ERROR, "index {} out of range for length {}", index, 
items.len());
+///     }
+///     Ok(items[index])
+/// }
+///
+/// assert_eq!(get(&[1, 2], 1)?, 2);
+/// assert_eq!(get(&[1, 2], 5).unwrap_err().message(), "index 5 out of range 
for length 2");
+/// # Ok::<(), tvm_ffi::Error>(())
+/// ```
 #[macro_export]
 macro_rules! bail {
     ($error_kind:expr, $fmt:expr $(, $args:expr)* $(,)?) => {{
@@ -152,17 +173,38 @@ macro_rules! bail {
     }};
 }
 
-/// Create a new error with file/line info attached
+/// Return an [`Error`](crate::Error) from the current function unless a
+/// condition holds.
 ///
-/// This macro automatically appends file/line info to the traceback
+/// `ensure!(cond, kind, fmt, args...)` is [`bail!`](crate::bail)`(kind, fmt,
+/// args...)` when `cond` is false.
 ///
 /// # Arguments
-/// * `kind` - The kind of the error
-/// * `msg` - The message of the error
-/// * `args` - The posisble format arguments
+///
+/// * `cond` - The condition that must hold.
+/// * `kind` - The kind of the error.
+/// * `fmt` - The format string of the message.
+/// * `args` - The format arguments.
 ///
 /// # Returns
-/// * `Result<(), Error>` - The result of the safe call
+///
+/// Returns `Err` of the error from the enclosing function when `cond` is
+/// false, and does nothing otherwise.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_ffi::{ensure, Result, VALUE_ERROR};
+///
+/// fn sqrt(x: f64) -> Result<f64> {
+///     ensure!(x >= 0.0, VALUE_ERROR, "sqrt of negative number {}", x);
+///     Ok(x.sqrt())
+/// }
+///
+/// assert_eq!(sqrt(4.0)?, 2.0);
+/// assert!(sqrt(-1.0).is_err());
+/// # Ok::<(), tvm_ffi::Error>(())
+/// ```
 #[macro_export]
 macro_rules! ensure {
     ($cond:expr, $error_kind:expr, $fmt:expr $(, $args:expr)* $(,)?) => {{
@@ -172,16 +214,32 @@ macro_rules! ensure {
     }};
 }
 
-/// Attach a context to a result if it is error
-///
-/// This macro automatically appends file/line info to the traceback
+/// Append the current source location to the backtrace of an `Err` result,
+/// and pass an `Ok` through.
 ///
 /// # Arguments
-/// * `error` - The error to attach the context to
-/// * `msg` - The message of the error
+///
+/// * `result` - The result to attach the context to.
 ///
 /// # Returns
-/// * `Result<(), Error>` - The result of the safe call
+///
+/// The result, with the location appended to an error's backtrace.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_ffi::{attach_context, bail, Result, RUNTIME_ERROR};
+///
+/// fn inner() -> Result<()> {
+///     bail!(RUNTIME_ERROR, "failed");
+/// }
+///
+/// fn outer() -> Result<()> {
+///     attach_context!(inner())
+/// }
+///
+/// assert!(outer().unwrap_err().backtrace().contains("outer"));
+/// ```
 #[macro_export]
 macro_rules! attach_context {
     ($error:expr) => {{
@@ -194,7 +252,7 @@ macro_rules! attach_context {
                     line!(),
                     $crate::function_name!()
                 );
-                Err(Error::with_appended_backtrace(error, &context))
+                Err($crate::error::Error::with_appended_backtrace(error, 
&context))
             }
         }
     }};
diff --git a/rust/tvm-ffi/src/object.rs b/rust/tvm-ffi/src/object.rs
index 7ca7ced5..73086ed4 100644
--- a/rust/tvm-ffi/src/object.rs
+++ b/rust/tvm-ffi/src/object.rs
@@ -288,16 +288,28 @@ pub fn is_instance_of<Target: 
ObjectCore>(object_type_index: i32) -> bool {
     }
 }
 
-/// Runtime-checked casting between arbitrary `ObjectRef` types.
+/// Runtime-checked casts between managed reference types, like C++
+/// `ObjectRef::as<T>`.
 ///
-/// The cast uses the target's [`AnyCompatible::check_any_strict`] 
implementation,
-/// mirroring the semantics of `ObjectRef::as<T>` in C++. This supports both
-/// object hierarchies and parameterized object containers.
-///
-/// This trait is blanket-implemented for every [`ObjectRefCore`] type that is
-/// also [`AnyCompatible`].
+/// Every managed reference implements this trait, so any reference can be
+/// cast to its parent, back to its own type, or to a parameterized container
+/// such as `Array<i64>`.
 pub trait ObjectRefCast: ObjectRefCore + AnyCompatible {
-    /// Consume `self` and rewrap the underlying object as `B` without copying.
+    /// Cast `self` to the reference type `B`, without copying the object.
+    ///
+    /// # Errors
+    ///
+    /// Returns a `TypeError` when the object is not a `B`.
+    ///
+    /// ```
+    /// use tvm_ffi::{object::ObjectRef, Array, ObjectRefCast};
+    /// let object: ObjectRef = Array::new(vec![1_i64]).try_cast()?;
+    /// let array: Array<i64> = object.try_cast()?;
+    /// assert_eq!(array.len(), 1);
+    /// # Ok::<(), tvm_ffi::Error>(())
+    /// ```
+    ///
+    /// See also [`Any::try_as`](crate::Any::try_as).
     #[inline(always)]
     fn try_cast<B>(self) -> crate::error::Result<B>
     where
diff --git a/rust/tvm-ffi/src/string.rs b/rust/tvm-ffi/src/string.rs
index d6aa82b5..352bb2d0 100644
--- a/rust/tvm-ffi/src/string.rs
+++ b/rust/tvm-ffi/src/string.rs
@@ -29,7 +29,17 @@ use tvm_ffi_sys::{TVMFFIAny, TVMFFIAnyDataUnion, 
TVMFFIByteArray, TVMFFIObject};
 //-----------------------------------------------------
 // Bytes
 //-----------------------------------------------------
-/// ABI stable Bytes container for ffi
+/// An immutable byte string, shared with C++ as `ffi.Bytes` and with Python
+/// as `bytes`.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_ffi::Bytes;
+///
+/// let bytes = Bytes::from(&b"abc"[..]);
+/// assert_eq!(bytes.as_slice(), b"abc");
+/// ```
 #[repr(C)]
 pub struct Bytes {
     data: TVMFFIAny,
@@ -224,7 +234,22 @@ impl Debug for Bytes {
 // String
 //-----------------------------------------------------
 
-/// ABI stable String container for ffi
+/// An immutable UTF-8 string, shared with C++ as `ffi.String` and with
+/// Python as `str`.
+///
+/// Short strings are stored inline in an [`Any`](crate::Any) without an
+/// allocation.
+///
+/// # Examples
+///
+/// ```
+/// use tvm_ffi::String;
+///
+/// let text = String::from("hello");
+/// assert_eq!(text.as_str(), "hello");
+/// assert_eq!(text.len(), 5);
+/// assert_eq!(text, "hello");
+/// ```
 #[repr(C)]
 pub struct String {
     data: TVMFFIAny,

Reply via email to