Skip to main content

DefaultOnEmptyRead

Struct DefaultOnEmptyRead 

Source
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 first DefaultOnEmptyRead onward 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>
where C: ConfigCore, T: SchemaRead<'de, C>, T::Dst: Default,

Source§

type Dst = <T as SchemaRead<'de, C>>::Dst

Source§

fn read( reader: impl Reader<'de>, dst: &mut MaybeUninit<Self::Dst>, ) -> ReadResult<()>

Read into dst from reader. Read more
Source§

const TYPE_META: TypeMeta = TypeMeta::Dynamic

Metadata about the type’s serialization. Read more
Source§

fn get(reader: impl Reader<'de>) -> ReadResult<Self::Dst>

Read Self::Dst from reader into a new Self::Dst.
Source§

impl<C, T> SchemaWrite<C> for DefaultOnEmptyRead<T>
where C: ConfigCore, T: SchemaWrite<C>,

Source§

const TYPE_META: TypeMeta

Metadata about the type’s serialization. Read more
Source§

type Src = <T as SchemaWrite<C>>::Src

Source§

fn size_of(src: &Self::Src) -> WriteResult<usize>

Get the serialized size of Self::Src. Read more
Source§

fn write(writer: impl Writer, src: &Self::Src) -> WriteResult<()>

Write Self::Src to writer.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<'de, T, C> Deserialize<'de, C> for T
where C: Config, T: SchemaRead<'de, C>,

Source§

fn deserialize(src: &'de [u8], config: C) -> ReadResult<Self::Dst>

Deserialize the input bytes into a new Self::Dst.
Source§

fn deserialize_into( src: &'de [u8], dst: &mut MaybeUninit<Self::Dst>, config: C, ) -> ReadResult<()>

Deserialize the input bytes into dst.
Source§

impl<'de, T> Deserialize<'de> for T
where T: SchemaRead<'de, Configuration>,

Source§

fn deserialize(src: &'de [u8]) -> ReadResult<Self::Dst>

Deserialize the input src bytes into a new Self::Dst.
Source§

fn deserialize_into( src: &'de [u8], dst: &mut MaybeUninit<Self::Dst>, ) -> ReadResult<()>

Deserialize the input src bytes into dst.
Source§

impl<T> DeserializeOwned for T

Source§

fn deserialize_from<'de>( src: impl Reader<'de>, ) -> ReadResult<<Self as SchemaRead<'de, DefaultConfig>>::Dst>

Deserialize from the given 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<()>

Deserialize from the given Reader into dst.
Source§

impl<T, C> DeserializeOwned<C> for T
where C: Config, T: SchemaReadOwned<C>,

Source§

fn deserialize_from<'de>( src: impl Reader<'de>, ) -> ReadResult<<Self as SchemaRead<'de, C>>::Dst>

Deserialize from the given 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<()>

Deserialize from the given Reader into dst.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, C> SchemaReadOwned<C> for T
where C: ConfigCore, T: for<'de> SchemaRead<'de, C>,

Source§

impl<T> Serialize for T

Source§

fn serialize(src: &Self::Src) -> WriteResult<Vec<u8>>

Serialize a serializable type into a Vec of bytes.
Source§

fn serialize_into(dst: impl Writer, src: &Self::Src) -> WriteResult<()>

Serialize a serializable type into the given byte buffer. Read more
Source§

fn serialized_size(src: &Self::Src) -> WriteResult<u64>

Get the size in bytes of the type when serialized.
Source§

impl<T, C> Serialize<C> for T
where C: Config, T: SchemaWrite<C> + ?Sized,

Source§

fn serialize(src: &Self::Src, config: C) -> WriteResult<Vec<u8>>

Serialize a serializable type into a Vec of bytes.
Source§

fn serialize_into( dst: impl Writer, src: &Self::Src, config: C, ) -> WriteResult<()>

Serialize a serializable type into the given Writer. Read more
Source§

fn serialized_size(src: &Self::Src, config: C) -> WriteResult<u64>

Get the size in bytes of the type when serialized.
Source§

impl<T, Target, C> TagEncoding<C> for T
where C: ConfigCore, T: for<'de> SchemaRead<'de, C, Dst = Target> + SchemaWrite<C, Src = Target> + 'static, Target: TryFrom<u32>, u32: TryFrom<Target>,

Source§

type Target = Target

Source§

fn try_from_u32( value: u32, ) -> Result<<T as TagEncoding<C>>::Target, TagEncodingOverflow>

Convert a u32 to the encoding target.
Source§

fn try_into_u32( x: <T as TagEncoding<C>>::Target, ) -> Result<u32, TagEncodingOverflow>

Convert the encoding target to a u32.
Source§

fn size_of_from_u32(value: u32) -> WriteResult<usize>

Get the size of the encoding target from the given u32. Read more
Source§

fn write_from_u32(writer: impl Writer, value: u32) -> WriteResult<()>

Write the encoding target from the given u32 to the given Writer. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.