FAQ
Frequently asked questions about facet.
General
What is facet?
facet is a reflection library for Rust. It provides runtime type information through a single derive macro, enabling serialization, pretty-printing, diffing, and more — all without code generation per format.
How is facet different from serde?
The key difference is in the approach:
- serde generates specialized code for each type × format combination (monomorphization)
- facet generates data (shapes) that format crates read at runtime
This means facet trades some speed for features: rich error diagnostics, one derive for many tools, and smaller binaries in format-heavy applications.
See Why facet? for a detailed comparison.
Is facet faster than serde?
No. serde's monomorphization allows the compiler to optimize serialization code per-type. facet uses runtime reflection, which is inherently slower.
If you're serializing millions of objects per second in a hot loop, serde is the better choice. For most applications, the difference is negligible and facet's features may be worth the tradeoff.
Does facet support no_std?
Yes. facet-core is no_std compatible. Format crates typically require alloc.
What rust version does facet require?
facet targets the latest stable Rust. Check the CI configuration for the current MSRV (minimum supported Rust version).
Why doesn't Shape implement Facet?
Shape contains types that cannot be serialized or deserialized:
- Function pointers — stored in attributes like
default,skip_serializing_if, andinvariants ConstTypeId— compile-time type identifiers that have no meaningful serialized form
If you're looking for a generic value type that can hold any facet-compatible data, use facet_value::Value.
If you genuinely need to compare, diff, or store shapes at runtime, see facet-shapelike. this crate implements a version of Shape called Shapelike which is fully owned and serializable, so you can send it between 2 completely random processes if you feel like it. Shapelike does not include any data that if sent to a seperate process would cause UB, like TypeId or the vtables that are included
Usage
How do I deserialize from JSON?
use facet:: Facet ;
use facet_json:: from_str;
# [ derive ( Facet )]
struct Person {
name : String ,
age : u32 ,
}
let person: Person = from_str ( r#"{"name": "Alice", "age": 30}"# ) ?;
How do I serialize to JSON?
use facet:: Facet ;
use facet_json:: to_string;
# [ derive ( Facet )]
struct Person {
name : String ,
age : u32 ,
}
let person = Person { name : "Alice" . into (), age : 30 };
let json = to_string ( & person);
How do I handle optional fields?
Use Option<T> and optionally skip_serializing_if:
# [ derive ( Facet )]
struct User {
name : String ,
# [ facet ( skip_serializing_if = Option :: is_none )]
email : Option < String >,
}
How do I rename fields?
Use rename for individual fields or rename_all for all fields:
# [ derive ( Facet )]
# [ facet ( rename_all = "camelCase" )]
struct Config {
# [ facet ( rename = "serverHost" )] // Override rename_all
host : String ,
server_port : u16 , // Becomes "serverPort"
}
How do I use a default value for missing fields?
# [ derive ( Facet )]
struct Config {
name : String ,
# [ facet ( default = 8080 )]
port : u16 ,
}
How do I skip a field?
# [ derive ( Facet )]
struct User {
name : String ,
# [ facet ( skip , default )] // Must have a default
internal_id : u64 ,
}
How do I flatten nested structs?
# [ derive ( Facet )]
struct Inner {
x : i32 ,
y : i32 ,
}
# [ derive ( Facet )]
struct Outer {
name : String ,
# [ facet ( flatten )]
coords : Inner ,
}
// Serializes as: {"name": "...", "x": 1, "y": 2}
How do I handle enums?
facet supports multiple enum representations:
// Externally tagged (default)
# [ derive ( Facet )]
enum Message {
Text ( String ),
Data { bytes : Vec < u8 > },
}
// {"Text": "hello"} or {"Data": {"bytes": [1, 2, 3]}}
// Internally tagged
# [ derive ( Facet )]
# [ facet ( tag = "type" )]
enum Event {
Click { x : i32 , y : i32 },
KeyPress { key : String },
}
// {"type": "Click", "x": 10, "y": 20}
// Adjacently tagged
# [ derive ( Facet )]
# [ facet ( tag = "t" , content = "c" )]
enum Value {
Int ( i32 ),
String ( String ),
}
// {"t": "Int", "c": 42}
// Untagged
# [ derive ( Facet )]
# [ facet ( untagged )]
enum AnyValue {
Int ( i32 ),
Float ( f64 ),
String ( String ),
}
// Just the value: 42 or 3.14 or "hello"
Error handling
Why do I get "unknown field" errors?
By default, facet ignores unknown fields. Add deny_unknown_fields to make them errors:
# [ derive ( Facet )]
# [ facet ( deny_unknown_fields )]
struct Config {
name : String ,
}
How do I get better error messages?
facet's format crates produce rich diagnostics with source locations when possible. Errors implement miette's Diagnostic trait for pretty printing:
use miette:: Result ;
use facet_json:: from_str;
fn main () -> Result <()> {
let config: Config = from_str ( input) ?;
Ok (())
}
How do I validate data after deserialization?
Use the invariants attribute:
# [ derive ( Facet )]
# [ facet ( invariants = validate )]
struct Port ( u16 );
fn validate ( port : & Port ) -> bool {
port. 0 > 0 && port. 0 < 65535
}
Compatibility
Can I use facet with serde?
Not directly. facet has its own derive macro and format crates. However, you can have both derives on a type:
# [ derive ( Facet , serde :: Serialize , serde :: Deserialize )]
struct MyType {
// ...
}
This lets you migrate incrementally or use serde for formats facet doesn't support yet.
Does facet support all serde attributes?
Most common attributes have equivalents. See Comparison with serde for a mapping.
Some serde features like #[serde(borrow)] don't have direct equivalents due to architectural differences.
Can I implement Facet manually?
Yes, but it's unsafe and requires careful attention to invariants. See the Contribute guide for details on the type system.
For most cases, use #[derive(Facet)] or #[facet(opaque)] for types that can't be derived.
Troubleshooting
"the trait Facet is not implemented for..."
Ensure the type either:
- Derives
Facet:#[derive(Facet)] - Has a built-in implementation (std types like
String,Vec<T>, etc.) - Is behind a feature flag (check crate docs)
"cannot find attribute facet in this scope"
Make sure you're using the derive macro:
use facet:: Facet ;
# [ derive ( Facet )] // This enables #[facet(...)] attributes
struct MyType { ... }
Compile errors mention extension attributes i'm not using
Extension attributes require importing the crate with an alias:
use facet_kdl as kdl; // Enables kdl:: prefix
# [ derive ( Facet )]
struct Config {
# [ facet ( kdl :: property )] // Now works
name : String ,
}
Without the import, kdl::property is not recognized.
Why do i get errors on the recursive data types
Due to the current facet implementation you need to manually tag where we should introduce indirection, this is done with the #[facet(recursive_type)] attribute like so
# [ derive ( Facet )]
struct Node {
# [ facet ( recursive_type )]
lhs : Option < Box < Node >>,
# [ facet ( recursive_type )]
rhs : Option < Box < Node >>,
}
Still have questions?
- Join the Discord to chat with the community
- Check the Showcases for more examples
- Browse the API documentation for detailed type information
- Open an issue if you've found a bug