Implementing Facet for third-party types
This guide is for contributing Facet implementations to the facet repository. If you just want to use a type that doesn't implement Facet, see When a type doesn't implement Facet.
Why we implement from the facet side
In Rust, you can only implement a trait in one of two places:
- The crate that defines the trait
- The crate that defines the type
Ideally, crates like chrono or uuid would implement Facet for their types directly. But facet isn't stable yet — the Facet trait and Shape structure are still evolving.
So we implement Facet for third-party types from the facet side, using optional features in facet-core (re-exported through facet). When facet stabilizes, crate authors can implement Facet themselves, and we'll deprecate our implementations.
Adding support for a new crate
Add the dependency to
facet-core/Cargo.toml:[ dependencies ] my-crate = { version = "1.0" , optional = true } [ features ] my-crate = [ "dep:my-crate" ] Create
facet-core/src/impls_my_crate.rsAdd to
facet-core/src/lib.rs:# [ cfg ( feature = "my-crate" )] mod impls_my_crate; Re-export the feature from
facet/Cargo.toml:[ features ] my-crate = [ "facet-core/my-crate" ]
Implementing Facet
Most third-party types are scalars (atomic values like UUIDs, timestamps, paths):
unsafe impl Facet < ' _ > for my_crate:: MyType {
const SHAPE : &' static Shape = & Shape {
id : Shape :: id_of ::< Self >(),
layout : Shape :: layout_of ::< Self >(),
vtable : value_vtable! ( my_crate:: MyType , |f, _opts| {
write! ( f, "MyType" )
}),
type_identifier : "MyType" ,
def : Def :: Scalar ,
ty : Type :: User ( UserType :: Opaque ),
type_params : & [],
doc : & [],
attributes : & [],
type_tag : None ,
inner : None ,
};
}
Look at existing implementations in facet-core/src/impls_* for patterns:
impls_uuid.rs— simple scalarimpls_chrono.rs— multiple related typesimpls_camino.rs— path types with borrowed variantsimpls_bytes.rs— byte buffer types
Collection types
Collections need vtable functions for their operations (push, get, len, etc.):
unsafe impl < T : Facet < ' static >> Facet < ' _ > for MyVec < T > {
const SHAPE : &' static Shape = & Shape {
id : Shape :: id_of ::< Self >(),
layout : Shape :: layout_of ::< Self >(),
vtable : value_vtable! ( MyVec <T >, |f, opts| {
write! ( f, "MyVec<" ) ?;
( T :: SHAPE . vtable . type_name )( f, opts) ?;
write! ( f, ">" )
}),
type_identifier : "MyVec" ,
def : Def :: List ( ListDef {
vtable : & ListVTable {
init_empty : |target| { /* ... */ },
push : |list, value| { /* ... */ },
len : |list| { /* ... */ },
get : |list, index| { /* ... */ },
},
item_shape : T :: SHAPE ,
}),
ty : Type :: User ( UserType :: Opaque ),
type_params : & [ TypeParam { name : "T" , shape : T :: SHAPE }],
doc : & [],
attributes : & [],
type_tag : None ,
inner : None ,
};
}
Testing
Add tests in the same file or in facet-core/tests/. Make sure to test:
- Round-trip through at least one format (JSON is easiest)
- Edge cases for the type (empty values, max values, etc.)