|  | # Ergonomic Inspect | 
|  |  | 
|  | This guide covers the usage of the | 
|  | [`fuchsia_inspect_derive`](/src/lib/diagnostics/inspect/derive) | 
|  | library, and assumes that you are familiar with | 
|  | [Inspect](/docs/development/diagnostics/inspect) | 
|  | and have basic experience with the | 
|  | [`fuchsia_inspect`](/src/lib/diagnostics/inspect/rust) library. | 
|  |  | 
|  | ## Overview | 
|  |  | 
|  | The `fuchsia_inspect_derive` library provides ergonomic macros, traits and | 
|  | smart pointers around the `fuchsia_inspect` library, that makes it easier to | 
|  | integrate inspect with your Rust code base, by: | 
|  |  | 
|  | - Owning source data and inspect data under the same RAII type | 
|  | - Being idiomatic. First class support for primitives, common interior | 
|  | mutability patterns and async. | 
|  | - Generating repetitive boilerplate code | 
|  | - Providing a unified way to [attach a type to inspect](#inspect-attaching) | 
|  | - Supporting gradual integration with existing code bases, both those that | 
|  | don't yet support inspect, and the ones that are integrated with | 
|  | `fuchsia_inspect` directly. | 
|  | - Supporting foreign types that lack inspect integration. See | 
|  | [`IDebug<T>`](#idebug) for usage and constraints. | 
|  |  | 
|  | At the same time, it preserves the performance and semantics of a manual inspect | 
|  | integration, by: | 
|  |  | 
|  | - Committing granular inspect tree modifications, where logical leaf nodes are | 
|  | updated independently. | 
|  | - Applying static dispatch only, to avoid additional runtime overhead. | 
|  | - Not using any additional synchronization primitives. | 
|  |  | 
|  | ### Caveats | 
|  |  | 
|  | When you integrate your Rust code base with this library, be aware that: | 
|  |  | 
|  | - The library mirrors the internal type hierarchy of the Rust program. Limited | 
|  | structural modifications such as renaming, flattening and omitting fields are | 
|  | supported (similar to [Serde][serde-field-attrs]). If the desired inspect tree | 
|  | structure is vastly different from the type hierarchy, you should consider | 
|  | using `fuchsia_inspect` directly. | 
|  | - Some features are not yet supported, requiring you to | 
|  | [implement `Inspect` manually](#implement-inspect-manually): | 
|  | - Lazy nodes, histograms and inspect arrays. | 
|  | - `Option<T>` and other enums. | 
|  | - Collection types, such as vectors and maps. | 
|  | - The library promotes [custom smart pointers](#iowned), which creates another | 
|  | layer of data wrapping. | 
|  |  | 
|  |  | 
|  | ## Quick start {#quick-start} | 
|  |  | 
|  | This section shows an example where you take an existing data structure and | 
|  | apply inspect to that structure. Let's start with a simple example, a Yak: | 
|  |  | 
|  | ```rust | 
|  | {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/diagnostics/inspect/rust-ergonomic/src/main.rs" region_tag="quick_start_before_decl" adjust_indentation="auto" %} | 
|  | ``` | 
|  |  | 
|  | Then, consider this construction site: | 
|  |  | 
|  | ```rust | 
|  | {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/diagnostics/inspect/rust-ergonomic/src/main.rs" region_tag="quick_start_before_init" adjust_indentation="auto" %} | 
|  | ``` | 
|  |  | 
|  | Let's make the yak inspectable. In particular: | 
|  |  | 
|  | - Expose the current hair length | 
|  | - Expose the number of times the Yak has been shaved | 
|  | - The credit card number should NOT be exposed | 
|  |  | 
|  | Now use `fuchsia_inspect_derive` to make this Yak inspectable: | 
|  |  | 
|  |  | 
|  | ```rust | 
|  | {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/diagnostics/inspect/rust-ergonomic/src/main.rs" region_tag="quick_start_after_decl" adjust_indentation="auto" %} | 
|  | ``` | 
|  |  | 
|  | Now, in your main program (or in a unit test), construct the yak and attach it | 
|  | to the inspect tree: | 
|  |  | 
|  |  | 
|  | ```rust | 
|  | {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/diagnostics/inspect/rust-ergonomic/src/main.rs" region_tag="quick_start_after_init" adjust_indentation="auto" %} | 
|  | ``` | 
|  |  | 
|  | Now you have integrated a simple program with Inspect. The rest of this guide | 
|  | describes the types, traits and macros of this library, and how to apply them to | 
|  | real world programs. | 
|  |  | 
|  | ## Derive `Inspect` {#inspect-derive} | 
|  |  | 
|  | `derive(Inspect)` can be added to any named struct, *but each of its fields | 
|  | must also implement `Inspect`* (except for `inspect_node` and skipped fields). | 
|  | The library provides implementations of `Inspect` for several types: | 
|  |  | 
|  | - The [`IOwned` smart pointers](#iowned) | 
|  | - Many common [interior mutability wrappers](#interior-mutability) | 
|  | - All inspect properties (`UintProperty`, `StringProperty`, etc) except for | 
|  | arrays and histograms | 
|  | - Other `Inspect` types. [See the section on nesting](#inspect-nesting). | 
|  |  | 
|  | If you add a type which isn't `Inspect`, you get a compiler error: | 
|  |  | 
|  | ```rust | 
|  | {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/diagnostics/inspect/rust-ergonomic/src/compiler_errors.rs" region_tag="derive_inspect_unwrapped" adjust_indentation="auto" %} | 
|  | ``` | 
|  |  | 
|  | ### Nested `Inspect` Types {#inspect-nesting} | 
|  |  | 
|  | `Inspect` types can be freely nested, like so: | 
|  |  | 
|  | ```rust | 
|  | {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/diagnostics/inspect/rust-ergonomic/src/main.rs" region_tag="inspect_nested_decl" adjust_indentation="auto" %} | 
|  | ``` | 
|  |  | 
|  | ### Fields and Attributes {#inspect-attributes} | 
|  |  | 
|  | All fields, except for skipped fields and `inspect_node`, must implement | 
|  | `Inspect`, either for `&mut T` or `&T`. | 
|  |  | 
|  | If an `inspect_node` field is present, instances will have its own node in the | 
|  | inspect tree. It must be a `fuchsia_inspect::Node`: | 
|  |  | 
|  | ```rust | 
|  | {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/diagnostics/inspect/rust-ergonomic/src/main.rs" region_tag="inspect_node_present_decl" adjust_indentation="auto" %} | 
|  | ``` | 
|  |  | 
|  | If `inspect_node` is absent, fields will be attached directly to the parent node | 
|  | (meaning that the name provided to `with_inspect` will be ignored): | 
|  |  | 
|  | ```rust | 
|  | {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/diagnostics/inspect/rust-ergonomic/src/main.rs" region_tag="inspect_node_absent_decl" adjust_indentation="auto" %} | 
|  | ``` | 
|  |  | 
|  | If your type needs to add or remove nodes or properties dynamically, | 
|  | it should own an inspect node. The inspect node is needed when | 
|  | nodes or properties are added or removed after the initial attachment. | 
|  |  | 
|  | `derive(Inspect)` supports the following field attributes: | 
|  |  | 
|  | - `inspect(skip)`: The field is ignored by inspect. | 
|  | - `inspect(rename = "foo")`: Use a different name. By default, the field name | 
|  | is used. | 
|  | - `inspect(forward)`: Forwards the attachment to an inner `Inspect` type, omitting one | 
|  | layer of nesting from the inspect hierarchy. The type must NOT have an | 
|  | `inspect_node` field. Useful for wrapper types. For example: | 
|  |  | 
|  | ```rust | 
|  | {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/diagnostics/inspect/rust-ergonomic/src/main.rs" region_tag="inspect_forward_decl" adjust_indentation="auto" %} | 
|  | ``` | 
|  |  | 
|  | ### Manually Managed Inspect Types | 
|  |  | 
|  | If you are integrating with a code base that uses `fuchsia_inspect` directly, | 
|  | its types are not be aware of `fuchsia_inspect_derive`. Do not add such | 
|  | manually managed types as fields to an `Inspect` type directly. Instead, | 
|  | [implement `Inspect` manually](#implement-inspect-manually) for the type. | 
|  | Avoid attaching manually outside of the `Inspect` trait, | 
|  | since attachment in `fuchsia_inspect_derive` occurs after construction. | 
|  | Attaching in a constructor can silently cause its inspect state to be | 
|  | absent. | 
|  |  | 
|  | ### Attaching to the Inspect Tree {#inspect-attaching} | 
|  |  | 
|  | An inspect type should be attached once, and immediately after instantiation, | 
|  | using the `with_inspect` extension trait method: | 
|  |  | 
|  | ```rust | 
|  | {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/diagnostics/inspect/rust-ergonomic/src/main.rs" region_tag="inspect_node_present_init" adjust_indentation="auto" %} | 
|  | ``` | 
|  |  | 
|  | If you have a nested `Inspect` structure, you should only attach the top-level | 
|  | type. The nested types are attached implicitly: | 
|  |  | 
|  | ```rust | 
|  | {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/diagnostics/inspect/rust-ergonomic/src/main.rs" region_tag="inspect_nested_init" adjust_indentation="auto" %} | 
|  | ``` | 
|  |  | 
|  | Note that when a `Yak` is constructed from within a `Stable`, there is no | 
|  | `with_inspect` call present. Instead, the `Yak` is automatically attached as a | 
|  | child of the `Stable`. However, you can still attach a `Yak` when it is the | 
|  | top-level type, such as in the unit tests for `Yak`. This allows you to test any | 
|  | `Inspect` type in isolation. | 
|  |  | 
|  | You can optionally choose to supply inspect nodes in constructors instead of | 
|  | explicitly calling `with_inspect` at the construction sites. First, ensure that | 
|  | the type is NOT nested under another `Inspect` type (as this would cause | 
|  | duplicate attachments). Sedondly, make sure to document this fact clearly, | 
|  | so the calling user is aware of your attachment convention. | 
|  |  | 
|  | ### Interior mutability {#interior-mutability} | 
|  |  | 
|  | In Rust (and particularly `async` Rust), it is common to use interior | 
|  | mutability. This library provides `Inspect` implementations for several smart | 
|  | pointers and locks: | 
|  |  | 
|  | - `std`: `Box`, `Arc`, `Rc`, `RefCell`, `Mutex` and `RwLock` | 
|  | - Note that `Cell` does NOT work. Instead, upgrade to a `RefCell`. | 
|  | - `parking_lot`: `Mutex` and `RwLock` | 
|  | - `futures`: `Mutex` | 
|  |  | 
|  | Generally, interior mutability within a `derive(Inspect)` type just works: | 
|  |  | 
|  | ```diff | 
|  | #[derive(Inspect)] | 
|  | struct Stable { | 
|  | -   yak: Yak, | 
|  | +   yak: Arc<Mutex<Yak>>, | 
|  | -   horse: Horse, | 
|  | +   horse: RefCell<Horse>, | 
|  | inspect_node: fuchsia_inspect::Node, | 
|  | } | 
|  | ``` | 
|  |  | 
|  | Make sure to put your smart pointers inside your mutability wrapper: | 
|  |  | 
|  | ```diff | 
|  | struct Yak { | 
|  | -   coins: IValue<Rc<RwLock<u32>>>,  // Won't compile | 
|  | +   coins: Rc<RwLock<IValue<u32>>>,  // Correct | 
|  | } | 
|  | ``` | 
|  |  | 
|  | If an inner type is behind a lock, attachment will fail if the lock is | 
|  | acquired by someone else. Hence, always attach immediately after | 
|  | instantiation. | 
|  |  | 
|  | ### Implement `Inspect` Manually {#implement-inspect-manually} | 
|  |  | 
|  | The `derive(Inspect)` derive-macro generates an | 
|  | `impl Inspect for &mut T { .. }`. Oftentimes, this works fine, but in | 
|  | some cases you may need to implement `Inspect` manually. Fortunately, | 
|  | the `Inspect` trait is quite simple: | 
|  |  | 
|  | ```rust | 
|  | trait Inspect { | 
|  | /// Attach self to the inspect tree | 
|  | fn iattach(self, parent: &Node, name: AsRef<str>) -> Result<(), AttachError>; | 
|  | } | 
|  | ``` | 
|  |  | 
|  | Do not return an `AttachError` for structural errors in the data. | 
|  | Instead, report the error using logs or an inspect node. | 
|  | `AttachError` is reserved for irrecoverable invariant errors that | 
|  | fail the entire attachment. | 
|  |  | 
|  | ## `IOwned` Smart Pointers {#iowned} | 
|  |  | 
|  | Smart pointers may sound scary, but you probably use them everyday already. For | 
|  | instance, `Arc` and `Box` are smart pointers. They are statically dispatched, | 
|  | and have first-class support in Rust (through [deref coercion]). This makes them | 
|  | minimally invasive. | 
|  |  | 
|  | `fuchsia_inspect_derive` comes with a few useful smart pointers that implement | 
|  | `Inspect` and can be used to wrap primitives, debuggable types, and more. They | 
|  | all share the same behavior: An `IOwned<T>` smart pointer owns a generic | 
|  | **source type** `T` and some associated **inspect data**. | 
|  |  | 
|  | Here is a demonstration of the `IOwned` API: | 
|  |  | 
|  | ```rust | 
|  | {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/diagnostics/inspect/rust-ergonomic/src/main.rs" region_tag="smart_pointers_ivalue" adjust_indentation="auto" %} | 
|  | ``` | 
|  |  | 
|  | An `IOwned<T>` smart pointer should not be instantiated directly, but rather one | 
|  | of its variants: | 
|  |  | 
|  | ### `IValue<T>` {#ivalue} | 
|  |  | 
|  | The `IValue<T>` smart pointer wraps a primitive ([or any type `T: Unit`](#unit)). | 
|  | For example, an `IValue<f32>` is represented as a `DoubleProperty`, and | 
|  | an `IValue<i16>` is represented as an `IntProperty`. | 
|  |  | 
|  | An `IValue` of a primitive results in the same structure as using a plain | 
|  | inspect property directly. So, why would you use an `IValue`? If you only | 
|  | need to write or increment a value, you can use a plain inspect property. If you | 
|  | also need to read the value, you should use an `IValue`. | 
|  |  | 
|  | ### `IDebug<T>` {#idebug} | 
|  |  | 
|  | The `IDebug<T>` smart pointer wraps a debuggable type, and maintains the debug | 
|  | representation of `T` as a `StringProperty`. This is useful for: | 
|  |  | 
|  | - Foreign types, where adding an inspect implementation is infeasible | 
|  | - Debugging, to quickly verify some state about your program | 
|  |  | 
|  | Avoid using debug representations in production code, since they come with | 
|  | the following issues: | 
|  |  | 
|  | - Debug representations are written on every inspect update, which can result in | 
|  | unnecessary performance overhead. | 
|  | - Debug representations can exhaust the space of the inspect VMO, causing | 
|  | truncation of the entire inspect state. | 
|  | - Debug representations cannot be integrated with the privacy pipeline: if any | 
|  | PII is exposed as part of the debug string, the entire field must be | 
|  | considered PII. Managing your own structured data allows to granularly redact | 
|  | fields containing PII. | 
|  |  | 
|  | ## The `Unit` Trait {#unit} | 
|  |  | 
|  | The `Unit` trait describes the inspect representation of a type, how to | 
|  | initialize it, and how to update it. It should be implemented for types that act | 
|  | as a logical leaf node, and does NOT support per-field updates. This library | 
|  | provides implementations of `Unit` for most primitives. For example, `u8`, | 
|  | `u16`, `u32` and `u64` are represented as a `UintProperty`. | 
|  |  | 
|  | ### Usage in IValue {#unit-usage} | 
|  |  | 
|  | A `Unit` type should be wrapped in an `IValue<T: Unit>` (see above), for a RAII | 
|  | managed inspectable type. It is NOT recommended to call methods on `Unit` | 
|  | directly. | 
|  |  | 
|  | ### Derive `Unit` {#unit-derive} | 
|  |  | 
|  | Sometimes a logical `Unit` is a composite type. Unit can be derived for a named | 
|  | struct, as long as its fields also implement `Unit`. For example: | 
|  |  | 
|  | ```rust | 
|  | {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/diagnostics/inspect/rust-ergonomic/src/main.rs" region_tag="unit_plain_decl" adjust_indentation="auto" %} | 
|  | ``` | 
|  |  | 
|  | `Unit` can be nested, but keep in mind that all fields are still written at | 
|  | the same time: | 
|  |  | 
|  | ```rust | 
|  | {% includecode gerrit_repo="fuchsia/fuchsia" gerrit_path="examples/diagnostics/inspect/rust-ergonomic/src/main.rs" region_tag="unit_nested_decl" adjust_indentation="auto" %} | 
|  | ``` | 
|  |  | 
|  | #### Attributes {#unit-attributes} | 
|  |  | 
|  | `derive(Unit)` supports the following field attributes: | 
|  |  | 
|  | - `inspect(skip)`: The field is ignored by inspect. | 
|  | - `inspect(rename = "foo")`: Use a different name. By default, the field name | 
|  | is used. | 
|  |  | 
|  | [deref coercion]: | 
|  | https://doc.rust-lang.org/1.27.2/book/second-edition/ch15-02-deref.html | 
|  |  | 
|  | [serde-field-attrs]: | 
|  | https://serde.rs/field-attrs.html |