pub trait SborEnumVariantFor<TEnum, X>where
TEnum: SborEnum<X>,
X: CustomValueKind,{
type VariantFields: SborTuple<X>;
type VariantFieldsRef<'a>: SborTuple<X>
where Self: 'a,
Self::VariantFields: 'a;
type OwnedVariant: IsSborFixedEnumVariant<Self::VariantFields>;
type BorrowedVariant<'a>: IsSborFixedEnumVariant<Self::VariantFieldsRef<'a>>
where Self: 'a,
Self::VariantFields: 'a;
const DISCRIMINATOR: u8;
const IS_FLATTENED: bool;
// Required methods
fn from_variant_fields(variant_fields: Self::VariantFields) -> Self;
fn as_variant_fields_ref(&self) -> Self::VariantFieldsRef<'_>;
fn into_enum(self) -> TEnum;
// Provided methods
fn as_encodable_variant<'a>(&'a self) -> Self::BorrowedVariant<'a> { ... }
fn from_decoded_variant(variant: Self::OwnedVariant) -> Self
where Self: Sized { ... }
}
Expand description
This trait is output for unique unskipped single children of enum variants, when
#[sbor(impl_variant_traits)]
is specified on an Enum or
#[sbor(impl_variant_trait)]
is specified on a single Enum variant.
It allows considering this type as representing an enum variant type under its parent enum. There are two flavours of how this embedding works in SBOR:
- In unflattened variants, it is a variant with fields (Self,)
- In flattened variants (only possible for tuple types) it is a variant with fields Self
This trait pairs well with the #[sbor(flatten)]
attribute, for implementing
the “enum variant is singleton struct type” pattern, which allows a number of benefits:
- A function can take or return a particular variant
- If code size is important (e.g. when building Scrypto), we want to enable pruning of
any serialization code we don’t need. Using
as_encodable_variant
andfrom_decoded_variant
avoids pulling in the parentTEnum
serialization code; and the serialization code for any unused types in other discriminators
§Note on generic parameter ordering
On this trait, we do not put X
first, as is normal with the SBOR traits.
Instead, TEnum
comes before X
so that the trait can be implemented on any foreign
type assuming TEnum
is local. This is so that the cryptic orphan rule
discussed in this stack overflow comment is satisfied https://stackoverflow.com/a/63131661
With this ordering we have P0 = X, T0 = Child Type (possibly foreign), T1 = TEnum, T2 = X, which passes the check.
Required Associated Constants§
const DISCRIMINATOR: u8
const IS_FLATTENED: bool
Required Associated Types§
Sourcetype VariantFields: SborTuple<X>
type VariantFields: SborTuple<X>
VariantFields is either Self
if IS_FLATTENED
else is (Self,)
Sourcetype VariantFieldsRef<'a>: SborTuple<X>
where
Self: 'a,
Self::VariantFields: 'a
type VariantFieldsRef<'a>: SborTuple<X> where Self: 'a, Self::VariantFields: 'a
VariantFieldsRef is either &Self
if IS_FLATTENED
else is (&Self,)
Sourcetype OwnedVariant: IsSborFixedEnumVariant<Self::VariantFields>
type OwnedVariant: IsSborFixedEnumVariant<Self::VariantFields>
This should always be SborFixedEnumVariant<{ [DISCRIMINATOR] as u8 }, Self::VariantFields>
§Why is this required as an associated type?
Ideally we’d not need this and just have as_encodable_variant
return
SborFixedEnumVariant<{ Self::DISCRIMINATOR as u8 }, Self::VariantFieldsRef<'_>>
But this gets “error: generic parameters may not be used in const operations”
§Why doesn’t this require VecDecode<X>
?
We don’t want a compiler error if a type only implements Categorize (and so gets this trait)
but not Decode. Really I’d like to say OwnedVariant: VecDecode<X> if Self: VecDecode<X>
but Rust doesn’t support that. Instead, users will need to add the bound on the associated
type themselves.
Sourcetype BorrowedVariant<'a>: IsSborFixedEnumVariant<Self::VariantFieldsRef<'a>>
where
Self: 'a,
Self::VariantFields: 'a
type BorrowedVariant<'a>: IsSborFixedEnumVariant<Self::VariantFieldsRef<'a>> where Self: 'a, Self::VariantFields: 'a
Should always be SborFixedEnumVariant<{ [DISCRIMINATOR] as u8 }, &'a Self::VariantFields>
§Why is this required as an associated type?
Ideally we’d not need this and just have as_encodable_variant
return
SborFixedEnumVariant<{ Self::DISCRIMINATOR }, Self::VariantFields>
But this gets “error: generic parameters may not be used in const operations” which needs
the const-generics
feature which has been in progress for a number of years.
See https://github.com/rust-lang/project-const-generics/issues/31
§Why doesn’t this require VecEncode<X>
?
We don’t want a compiler error if a type only implements Categorize (and so gets this trait)
but not Encode. Really I’d like to say BorrowedVariant<'a>: VecEncode<X> if Self: VecEncode<X>
but Rust doesn’t support that. Instead, users will need to add the bound on the associated
type themselves.
Instead, you can express a trait bound as such:
pub trait MyNewSuperTrait:
for<'a> SborEnumVariantFor<
TEnum,
X,
OwnedVariant: ManifestDecode,
BorrowedVariant<'a>: ManifestEncode,
>
{}
Required Methods§
fn from_variant_fields(variant_fields: Self::VariantFields) -> Self
fn as_variant_fields_ref(&self) -> Self::VariantFieldsRef<'_>
fn into_enum(self) -> TEnum
Provided Methods§
Sourcefn as_encodable_variant<'a>(&'a self) -> Self::BorrowedVariant<'a>
fn as_encodable_variant<'a>(&'a self) -> Self::BorrowedVariant<'a>
Can be used to encode the type as a variant under TEnum
, like this:
encoder.encode(x.as_encodable_variant())
.
To use this pattern in a generic context, you will likely need to add a bound like
for<'a> T::BorrowedVariant<'a>: VecEncode<X>
.
Sourcefn from_decoded_variant(variant: Self::OwnedVariant) -> Selfwhere
Self: Sized,
fn from_decoded_variant(variant: Self::OwnedVariant) -> Selfwhere
Self: Sized,
Can be used to decode the type from an encoded variant, like this:
T::from_decoded_variant(decoder.decode()?)
.
To use this pattern in a generic context, you will likely need to add a bound like
T::OwnedVariant: VecDecode<X>
.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.