Shape
What you can get from Shape
Identity
ConstTypeId— A stable, hashable identifier for the typetype_identifier()— Human-readable name (e.g., "MyStruct", "Vec") - Generics metadata — Information about generic type parameters
Layout
size()— Size in bytesalignment()— Alignment requirementownedvsborrowed— Whether the type owns or borrows its data
Structure
Shapecontains:Type— Structural classification (Struct, Enum, Primitive, Pointer, etc.)Def— Semantic definition (Scalar, List, Map, Struct, Enum, etc.)- Fields/variants/docstrings — For aggregate types
- Attributes — Including
skip_unless_truthy,sensitive, custom extension attributes
Access fields through
PeekorPartialfor safe, ergonomic inspection and mutation.VTables
Operations available on this type via function pointers:
clone_into— Runtime cloning without Clone bounddisplay/debug— Formattingparse— Parsing from stringshash— Computing hashespartial_eq— Equality comparisontruthiness_fn()— Checking if a value is truthy (when available)
Use
Characteristicto query support for these operations:if shape. is ( Characteristic :: Clone ) { // Safe to call clone_into from the vtable } Truthiness
Types can register a truthiness predicate — a function that determines if a value is "truthy" or "falsy". This is used by
#[facet(skip_unless_truthy)]to conditionally skip serialization.Call
shape.truthiness_fn()to get the predicate, if available:if let Some ( truthy) = shape. truthiness_fn () { let is_truthy =unsafe { truthy ( ptr) }; // Use is_truthy to decide whether to serialize } Built-in truthiness rules:
- bool:
trueis truthy - Numbers: non-zero is truthy (floats also exclude NaN)
- Collections (Vec, String, slice, etc.): non-empty is truthy
- Option:
Some(_)is truthy,Noneis falsy - Arrays: non-zero-length arrays are truthy
- Custom types: Can register a custom truthiness function via
#[facet(truthy = path::to::fn)]
Safety
Why
Facetisunsafe:- Facet requires you to ensure layout matches between Rust and the shape
- Pointers in the shape (vtable, type_ops) must be valid
- When implementing custom Facet, you're responsible for correctness
Invariants you must respect when consuming
Shape:- Don't call vtable functions with mismatched pointer types
- Don't assume a type has a characteristic it doesn't claim
- Truthiness predicates assume well-formed values
Examples
Listing fields:
use facet:: Peek ; let value =MyStruct { /* ... */ }; let peek =Peek :: new ( & value); for fieldin peek. fields () { println! ( "Field: {}" , field. name ()); } Checking marker traits:
if shape. is ( Characteristic :: Clone ) { // This type is Clone } Rendering type names:
println! ( "Type: {}" , shape. type_identifier ()); Checking truthiness support:
if let Some ( truthy) = shape. truthiness_fn () { let is_truthy =unsafe { truthy ( ptr) }; }