pub trait TagEncoding<C: ConfigCore>:
for<'de> SchemaRead<'de, C, Dst = Self::Target>
+ SchemaWrite<C, Src = Self::Target>
+ 'static {
type Target;
// Required methods
fn try_from_u32(value: u32) -> Result<Self::Target, TagEncodingOverflow>;
fn try_into_u32(x: Self::Target) -> Result<u32, TagEncodingOverflow>;
// Provided methods
fn size_of_from_u32(value: u32) -> WriteResult<usize> { ... }
fn write_from_u32(writer: impl Writer, value: u32) -> WriteResult<()> { ... }
}Expand description
Tag encoding trait.
This trait normalizes discriminant encodings to a common type, u32.
The reason for this is that SchemaRead and SchemaWrite implementations
for enums need to match on explicit integer literals.
For example,
enum Foo {
Bar,
Baz,
}
unsafe impl<'de, C: Config> SchemaRead<'de, C> for Foo {
type Dst = Self;
fn read(reader: impl Reader<'de>, dst: &mut MaybeUninit<Self::Dst>) -> ReadResult<()> {
let tag = C::TagEncoding::get(reader)?;
// Cannot match a generic type with an integer literal.
match tag {
0 => {}
1 => {}
// ...
}
Ok(())
}
}It is not possible to match on a generic type with an integer literal. This is because we have no way of telling the compiler that a trait is represented by a closed set of integer types.
By normalizing the discriminant encoding to a common type, we can get around this limitation.
enum Foo {
Bar,
Baz,
}
unsafe impl<'de, C: Config> SchemaRead<'de, C> for Foo {
type Dst = Self;
fn read(reader: impl Reader<'de>, dst: &mut MaybeUninit<Self::Dst>) -> ReadResult<()> {
let tag = C::TagEncoding::try_into_u32(C::TagEncoding::get(reader)?)?;
// Now we can match on integer literals.
match tag {
0 => {
// ...
}
1 => {
// ...
}
_ => {
return Err(invalid_tag_encoding(tag as usize));
}
}
Ok(())
}
}A note on performance: in release builds, the generated assembly for this scheme
typically elides conversions to and from the intermediate u32 type.
Because TagEncoding is ultimately monomorphized into concrete integer targets,
the result of try_from/try_into calls are known at compile time.
All reads, matches, and writes on enums in the crate (including derive macros)
use compile-time integer literals, and in practice it was observed that tags are
loaded at their original width and compared directly to immediates.
Required Associated Types§
Required Methods§
Sourcefn try_from_u32(value: u32) -> Result<Self::Target, TagEncodingOverflow>
fn try_from_u32(value: u32) -> Result<Self::Target, TagEncodingOverflow>
Convert a u32 to the encoding target.
Sourcefn try_into_u32(x: Self::Target) -> Result<u32, TagEncodingOverflow>
fn try_into_u32(x: Self::Target) -> Result<u32, TagEncodingOverflow>
Convert the encoding target to a u32.
Provided Methods§
Sourcefn size_of_from_u32(value: u32) -> WriteResult<usize>
fn size_of_from_u32(value: u32) -> WriteResult<usize>
Get the size of the encoding target from the given u32.
The u32 will be converted to the encoding target before calling
SchemaWrite::size_of on the target implementation.
Sourcefn write_from_u32(writer: impl Writer, value: u32) -> WriteResult<()>
fn write_from_u32(writer: impl Writer, value: u32) -> WriteResult<()>
Write the encoding target from the given u32 to the given Writer.
The u32 will be converted to the encoding target before calling
SchemaWrite::write on the target implementation.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".