//! By default, serde does not know how to parse fixed length arrays of sizes //! that aren't common (I.E: 32) Here we provide a module that can be used to //! serialize arrays that relies on const generics. //! //! Usage: //! //! ```rust,ignore` //! #[derive(Serialize)] //! struct Example { //! #[serde(with = "array")] //! array: [u8; 55], //! } //! ``` use { serde::{Deserialize, Serialize, Serializer}, std::mem::MaybeUninit, }; /// Serialize an array of size N using a const generic parameter to drive serialize_seq. pub fn serialize(array: &[T; N], serializer: S) -> Result where S: Serializer, T: Serialize, { use serde::ser::SerializeTuple; let mut seq = serializer.serialize_tuple(N)?; array.iter().try_for_each(|e| seq.serialize_element(e))?; seq.end() } /// A visitor that carries type-level information about the length of the array we want to /// deserialize. struct ArrayVisitor { _marker: std::marker::PhantomData, } /// Implement a Visitor over our ArrayVisitor that knows how many times to /// call next_element using the generic. impl<'de, T, const N: usize> serde::de::Visitor<'de> for ArrayVisitor where T: Deserialize<'de>, { type Value = [T; N]; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { write!(formatter, "an array of length {N}") } fn visit_seq(self, mut seq: A) -> Result where A: serde::de::SeqAccess<'de>, { // We use MaybeUninit to allocate the right amount of memory // because we do not know if `T` has a constructor or a default. // Without this we would have to allocate a Vec. let mut array = MaybeUninit::<[T; N]>::uninit(); let ptr = array.as_mut_ptr() as *mut T; let mut pos = 0; while pos < N { let next = seq .next_element()? .ok_or_else(|| serde::de::Error::invalid_length(pos, &self))?; unsafe { std::ptr::write(ptr.add(pos), next); } pos += 1; } // We only succeed if we fully filled the array. This prevents // accidentally returning garbage. if pos == N { return Ok(unsafe { array.assume_init() }); } Err(serde::de::Error::invalid_length(pos, &self)) } } /// Deserialize an array with an ArrayVisitor aware of `N` during deserialize. pub fn deserialize<'de, D, T, const N: usize>(deserializer: D) -> Result<[T; N], D::Error> where D: serde::Deserializer<'de>, T: serde::de::Deserialize<'de>, { deserializer.deserialize_tuple( N, ArrayVisitor { _marker: std::marker::PhantomData, }, ) }