Return a type that can be converted to Vec, slice, or array

Hi! I am trying to implement a field attribute for serde that works with Vec<some struct>, [some struct; N], and &[some struct]. So far I have the following code (see Rust Playground):

use serde_json;
use serde::{de, ser::SerializeSeq, Deserializer, Deserialize, Serializer, Serialize};

struct Amount(u64);
impl Amount {
    fn as_u64(&self) -> u64 {
        self.0
    }
}

trait SerdeAmountForSlice {
    fn ser_u64_slice<S: SerializeSeq>(self, s: &mut S) -> Result<(), S::Error>;
}
impl SerdeAmountForSlice for Amount {
    fn ser_u64_slice<S: SerializeSeq>(self, s: &mut S) -> Result<(), S::Error> {
        s.serialize_element(&self.as_u64())
    }
}

mod custom {
    use super::*;
    
    pub fn serialize<A: SerdeAmountForSlice, S: Serializer>(
        a_slice: &[A],
        s: S,
    ) -> Result<S::Ok, S::Error> {
        let mut seq = s.serialize_seq(Some(a_slice.len()))?;
        for e in a_slice {
            e.ser_u64_slice(&mut seq)?;
        }
        seq.end()
    }
    
    pub fn deserialize<'d, A: SerdeAmountForSlice, D: Deserializer<'d>>(
        d: D,
    ) -> Result<&'d [A], D::Error> {
        
    }
}

#[derive(Serialize, Deserialize)]
struct Lol<'a> {
    #[serde(with = "custom")]
    amt1: Vec<Amount>,
    #[serde(with = "custom")]
    amt2: [Amount; 1],
    #[serde(with = "custom")]
    amt3: &'a [Amount],
}

fn main() {
    let lol = Lol {
        amt1: vec![Amount(1), Amount(2)],
        amt2: [Amount(3)],
        amt3: &[Amount(3), Amount(4), Amount(5)],
    };
}

But I am getting the following errors:

rror[E0308]: mismatched types
  --> src/main.rs:36:10
   |
34 |     pub fn deserialize<'d, A: SerdeAmountForSlice, D: Deserializer<'d>>(
   |            ----------- implicitly returns `()` as its body has no tail or `return` expression
35 |         d: D,
36 |     ) -> Result<&'d [A], D::Error> {
   |          ^^^^^^^^^^^^^^^^^^^^^^^^^ expected enum `Result`, found `()`
   |
   = note:   expected enum `Result<&'d [A], <D as _::_serde::Deserializer<'d>>::Error>`
           found unit type `()`

error[E0308]: mismatched types
  --> src/main.rs:41:21
   |
41 | #[derive(Serialize, Deserialize)]
   |                     ^^^^^^^^^^^- help: try using a conversion method: `.to_vec()`
   |                     |
   |                     expected struct `Vec`, found `&[_]`
   |
   = note: expected struct `Vec<Amount>`
           found reference `&[_]`
   = note: this error originates in the macro `try` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0308]: mismatched types
  --> src/main.rs:41:21
   |
41 | #[derive(Serialize, Deserialize)]
   |                     ^^^^^^^^^^^ expected array of 1 element, found `&[_]`
   |
   = note:  expected array `[Amount; 1]`
           found reference `&[_]`
   = note: this error originates in the macro `try` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'d` due to conflicting requirements
  --> src/main.rs:41:21
   |
41 | #[derive(Serialize, Deserialize)]
   |                     ^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'de` as defined here...
  --> src/main.rs:41:21
   |
41 | #[derive(Serialize, Deserialize)]
   |                     ^^^^^^^^^^^
note: ...so that the types are compatible
  --> src/main.rs:41:21
   |
41 | #[derive(Serialize, Deserialize)]
   |                     ^^^^^^^^^^^
   = note: expected `_::_serde::Deserializer<'_>`
              found `_::_serde::Deserializer<'de>`
note: but, the lifetime must be valid for the lifetime `'a` as defined here...
  --> src/main.rs:42:12
   |
42 | struct Lol<'a> {
   |            ^^
note: ...so that the types are compatible
  --> src/main.rs:41:21
   |
41 | #[derive(Serialize, Deserialize)]
   |                     ^^^^^^^^^^^
   = note: expected `Result<<__Visitor<'de, 'a> as Visitor<'de>>::visit_seq::__DeserializeWith<'de, 'a>, _>`
              found `Result<<__Visitor<'de, 'a> as Visitor<'de>>::visit_seq::__DeserializeWith<'_, '_>, _>`
   = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)

error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'d` due to conflicting requirements
  --> src/main.rs:41:21
   |
41 | #[derive(Serialize, Deserialize)]
   |                     ^^^^^^^^^^^
   |
note: first, the lifetime cannot outlive the lifetime `'de` as defined here...
  --> src/main.rs:41:21
   |
41 | #[derive(Serialize, Deserialize)]
   |                     ^^^^^^^^^^^
note: ...so that the types are compatible
  --> src/main.rs:41:21
   |
41 | #[derive(Serialize, Deserialize)]
   |                     ^^^^^^^^^^^
   = note: expected `_::_serde::Deserializer<'_>`
              found `_::_serde::Deserializer<'de>`
note: but, the lifetime must be valid for the lifetime `'a` as defined here...
  --> src/main.rs:42:12
   |
42 | struct Lol<'a> {
   |            ^^
note: ...so that the types are compatible
  --> src/main.rs:41:21
   |
41 | #[derive(Serialize, Deserialize)]
   |                     ^^^^^^^^^^^
   = note: expected `Result<<__Visitor<'de, 'a> as Visitor<'de>>::visit_map::__DeserializeWith<'de, 'a>, _>`
              found `Result<<__Visitor<'de, 'a> as Visitor<'de>>::visit_map::__DeserializeWith<'_, '_>, _>`
   = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)

How to solve it?

Try implementing serialization first. Remove Deserialize from derive. That will let you tackle one problem at a time.

With deserialization you'll most likely find that it's impossible to implement it for slices. &[] slices in Rust are a case of temporarily borrowed memory. It's not a type that can be made out of nothing. It's not something that can be just created and returned from a function. Slices are only pointing to a data that has already existed before, and this data has to already be in the type/format that the slice has. A function can only return &[Amount] if it already got &[Amount] as an input argument[1], and you won't get that in Serde. Deserializing to a slice is a very limited case that most of the time is logically impossible.


  1. 'static and leaked memory are technically exceptions to this, but they are not real solutions ↩ī¸Ž

5 Likes

Oh I see. Serialize only needed a change in the trait method to accept &self instead of self to work, this was an error from the copy and paste.

Amount the slice, it makes sense, I got confused for a moment :stuck_out_tongue: thanks