pub struct DefaultOnEmptyRead<T>(/* private fields */);Expand description
Deserializes using the wire schema T normally, but yields
T::Dst::default() when the reader is exhausted (hits EOF) instead of
erroring.
The purpose is backward compatibility when new fields are appended to the
tail of a persisted struct: older, shorter encodings that predate those
fields still decode, with the missing trailing fields filled from their
Default value. Reading a full encoding is unaffected.
Writing is unchanged — the value is serialized exactly as T would,
producing bytes that decode identically with or without this adapter. Only
the read path differs, so this is purely a decode-time compatibility shim.
Apply it via the with attribute on the (necessarily trailing) fields it
covers, naming the field’s own schema as T:
#[derive(SchemaWrite, SchemaRead, Debug, PartialEq)]
struct Record {
id: u32,
// Appended in a later version; older encodings omit it entirely.
#[wincode(with = "DefaultOnEmptyRead<u64>")]
added_later: u64,
}
// A full encoding round-trips as usual.
let record = Record { id: 7, added_later: 42 };
let bytes = wincode::serialize(&record).unwrap();
assert_eq!(record, wincode::deserialize(&bytes).unwrap());
// An older encoding that predates `added_later` decodes to its default.
let legacy = wincode::serialize(&7u32).unwrap();
let decoded: Record = wincode::deserialize(&legacy).unwrap();
assert_eq!(decoded, Record { id: 7, added_later: 0 });§Warning
The fallback is driven purely by running out of bytes, so it is only sound where “no more bytes” unambiguously means “this optional tail is absent”:
- Do not use it on sequence elements. When more items follow, a missing field does not produce EOF — the read simply continues into the bytes that encode the next item. Instead of defaulting the absent field, the decoder consumes the following item’s data, desynchronizing the rest of the sequence. The fallback only helps when the missing bytes are genuinely the end of input.
- Do not use it on a middle field followed by an always-present field.
The fallback catches any size-limit error from
T, including a partially-present value, so a genuinely truncated field is masked instead of reported and the fields after it are misaligned. It is only safe on a trailing run of fields where every field from the firstDefaultOnEmptyReadonward is itself optional-on-EOF.
Prefer applying it to trailing fields of a top-level struct decoded with
deserialize_exact, where reaching EOF exactly
at a field boundary is well defined and the end-of-input check is
straightforward and cheap.
Trait Implementations§
Source§impl<'de, C, T> SchemaRead<'de, C> for DefaultOnEmptyRead<T>
impl<'de, C, T> SchemaRead<'de, C> for DefaultOnEmptyRead<T>
Source§impl<C, T> SchemaWrite<C> for DefaultOnEmptyRead<T>where
C: ConfigCore,
T: SchemaWrite<C>,
impl<C, T> SchemaWrite<C> for DefaultOnEmptyRead<T>where
C: ConfigCore,
T: SchemaWrite<C>,
Auto Trait Implementations§
impl<T> Freeze for DefaultOnEmptyRead<T>where
PhantomData<T>: Freeze,
impl<T> RefUnwindSafe for DefaultOnEmptyRead<T>where
PhantomData<T>: RefUnwindSafe,
impl<T> Send for DefaultOnEmptyRead<T>where
PhantomData<T>: Send,
impl<T> Sync for DefaultOnEmptyRead<T>where
PhantomData<T>: Sync,
impl<T> Unpin for DefaultOnEmptyRead<T>where
PhantomData<T>: Unpin,
impl<T> UnsafeUnpin for DefaultOnEmptyRead<T>where
PhantomData<T>: UnsafeUnpin,
impl<T> UnwindSafe for DefaultOnEmptyRead<T>where
PhantomData<T>: UnwindSafe,
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<'de, T, C> Deserialize<'de, C> for Twhere
C: Config,
T: SchemaRead<'de, C>,
impl<'de, T, C> Deserialize<'de, C> for Twhere
C: Config,
T: SchemaRead<'de, C>,
Source§fn deserialize(src: &'de [u8], config: C) -> ReadResult<Self::Dst>
fn deserialize(src: &'de [u8], config: C) -> ReadResult<Self::Dst>
Self::Dst.Source§fn deserialize_into(
src: &'de [u8],
dst: &mut MaybeUninit<Self::Dst>,
config: C,
) -> ReadResult<()>
fn deserialize_into( src: &'de [u8], dst: &mut MaybeUninit<Self::Dst>, config: C, ) -> ReadResult<()>
dst.Source§impl<'de, T> Deserialize<'de> for Twhere
T: SchemaRead<'de, Configuration>,
impl<'de, T> Deserialize<'de> for Twhere
T: SchemaRead<'de, Configuration>,
Source§fn deserialize(src: &'de [u8]) -> ReadResult<Self::Dst>
fn deserialize(src: &'de [u8]) -> ReadResult<Self::Dst>
src bytes into a new Self::Dst.Source§fn deserialize_into(
src: &'de [u8],
dst: &mut MaybeUninit<Self::Dst>,
) -> ReadResult<()>
fn deserialize_into( src: &'de [u8], dst: &mut MaybeUninit<Self::Dst>, ) -> ReadResult<()>
src bytes into dst.Source§impl<T> DeserializeOwned for Twhere
T: SchemaReadOwned<Configuration>,
impl<T> DeserializeOwned for Twhere
T: SchemaReadOwned<Configuration>,
Source§fn deserialize_from<'de>(
src: impl Reader<'de>,
) -> ReadResult<<Self as SchemaRead<'de, DefaultConfig>>::Dst>
fn deserialize_from<'de>( src: impl Reader<'de>, ) -> ReadResult<<Self as SchemaRead<'de, DefaultConfig>>::Dst>
Reader into a new Self::Dst.Source§fn deserialize_from_into<'de>(
src: impl Reader<'de>,
dst: &mut MaybeUninit<<Self as SchemaRead<'de, DefaultConfig>>::Dst>,
) -> ReadResult<()>
fn deserialize_from_into<'de>( src: impl Reader<'de>, dst: &mut MaybeUninit<<Self as SchemaRead<'de, DefaultConfig>>::Dst>, ) -> ReadResult<()>
Reader into dst.Source§impl<T, C> DeserializeOwned<C> for Twhere
C: Config,
T: SchemaReadOwned<C>,
impl<T, C> DeserializeOwned<C> for Twhere
C: Config,
T: SchemaReadOwned<C>,
Source§fn deserialize_from<'de>(
src: impl Reader<'de>,
) -> ReadResult<<Self as SchemaRead<'de, C>>::Dst>
fn deserialize_from<'de>( src: impl Reader<'de>, ) -> ReadResult<<Self as SchemaRead<'de, C>>::Dst>
Reader into a new Self::Dst.Source§fn deserialize_from_into<'de>(
src: impl Reader<'de>,
dst: &mut MaybeUninit<<Self as SchemaRead<'de, C>>::Dst>,
) -> ReadResult<()>
fn deserialize_from_into<'de>( src: impl Reader<'de>, dst: &mut MaybeUninit<<Self as SchemaRead<'de, C>>::Dst>, ) -> ReadResult<()>
Reader into dst.impl<T, C> SchemaReadOwned<C> for Twhere
C: ConfigCore,
T: for<'de> SchemaRead<'de, C>,
Source§impl<T> Serialize for T
impl<T> Serialize for T
Source§fn serialize(src: &Self::Src) -> WriteResult<Vec<u8>>
fn serialize(src: &Self::Src) -> WriteResult<Vec<u8>>
Vec of bytes.Source§fn serialize_into(dst: impl Writer, src: &Self::Src) -> WriteResult<()>
fn serialize_into(dst: impl Writer, src: &Self::Src) -> WriteResult<()>
Source§fn serialized_size(src: &Self::Src) -> WriteResult<u64>
fn serialized_size(src: &Self::Src) -> WriteResult<u64>
Source§impl<T, C> Serialize<C> for T
impl<T, C> Serialize<C> for T
Source§fn serialize(src: &Self::Src, config: C) -> WriteResult<Vec<u8>>
fn serialize(src: &Self::Src, config: C) -> WriteResult<Vec<u8>>
Vec of bytes.Source§fn serialize_into(
dst: impl Writer,
src: &Self::Src,
config: C,
) -> WriteResult<()>
fn serialize_into( dst: impl Writer, src: &Self::Src, config: C, ) -> WriteResult<()>
Source§fn serialized_size(src: &Self::Src, config: C) -> WriteResult<u64>
fn serialized_size(src: &Self::Src, config: C) -> WriteResult<u64>
Source§impl<T, Target, C> TagEncoding<C> for Twhere
C: ConfigCore,
T: for<'de> SchemaRead<'de, C, Dst = Target> + SchemaWrite<C, Src = Target> + 'static,
Target: TryFrom<u32>,
u32: TryFrom<Target>,
impl<T, Target, C> TagEncoding<C> for Twhere
C: ConfigCore,
T: for<'de> SchemaRead<'de, C, Dst = Target> + SchemaWrite<C, Src = Target> + 'static,
Target: TryFrom<u32>,
u32: TryFrom<Target>,
type Target = Target
Source§fn try_from_u32(
value: u32,
) -> Result<<T as TagEncoding<C>>::Target, TagEncodingOverflow>
fn try_from_u32( value: u32, ) -> Result<<T as TagEncoding<C>>::Target, TagEncodingOverflow>
u32 to the encoding target.Source§fn try_into_u32(
x: <T as TagEncoding<C>>::Target,
) -> Result<u32, TagEncodingOverflow>
fn try_into_u32( x: <T as TagEncoding<C>>::Target, ) -> Result<u32, TagEncodingOverflow>
u32.Source§fn size_of_from_u32(value: u32) -> WriteResult<usize>
fn size_of_from_u32(value: u32) -> WriteResult<usize>
u32. Read more