Search code examples
rustlifetimeserde

Rust JSON serialization/deserialization with serde/serde_json whilst using generics and lifetimes?


This question is distinct from serde_json with deserialize and lifetimes for generic function because neither from_slice or from_str work, and the error messages are different.

This question is distinct from serde/rust deserialize JSON into Hashmap, since they don't run into issues with trait bounds.

This question is distinct from Lifetime error with Serde in a Generic Function because I still run into the issue when I change Deserialize into DeserializeOwned or I implement the higher-rank trait bounds.


My goal is very simple: create a struct wrapper around a range map (crate: rangemap) (which is already serializable with the serde1 feature) that allows me to save the data to a file.

I also want it to call the save function when the struct gets dropped (by impl-ing Drop) The following code is missing some obvious checks (whether the file location is valid) for the sake for brevity.

Struct definition:

    use serde::{Deserialize, Serialize};
    use serde_json;
    use rangemap::RangeMap;
    use serde::de::DeserializeOwned;
    #[derive(Debug, Clone, Serialize, Deserialize)]
    pub struct RangeDataStorage<K, V> 
        where
            K: Ord + Clone + Eq + Debug + Default + Serialize + Deserialize,
            V: Default + Serialize + Debug + Eq + Clone + Deserialize {
        range_map: RangeMap<K, V>,
    }

I've gone through a few iterations of the struct impls:

Struct impl:

    impl<K, V> RangeDataStorage<K, V>
        where
            K: Ord + Clone + Eq + Debug + Default + Serialize + Deserialize,
            V: Default + Serialize + Debug + Eq + Clone + Deserialize{
        pub fn new(location: Option<String>) -> Result<RangeDataStorage<K, V>, Box<dyn Error>> {
            if location.is_some() {
                // load from file
                let location = location.unwrap();
                let data = std::fs::read_to_string(location)?;
                let data: RangeDataStorage<K, V> = serde_json::from_str(&data)?;
                let data = data.clone(); // does not fix problem
                return Ok(data);
            }
            Ok(
                RangeDataStorage {
                    range_map: RangeMap::new(),
                }
            )
        }
    
        pub fn insert(&mut self, key_start: K, key_end: K, value: V) {
            let range = Range {
                start: key_start,
                end: key_end,
            };
            self.range_map.insert(range, value);
        }
    
        pub fn save(&mut self, location: String) {
            let file = std::fs::File::create(location).unwrap();
            let writer = std::io::BufWriter::new(file);
            let res = serde_json::to_writer(writer, self);
            match res {
                Ok(_) => {},
                Err(e) => { println!("Error while saving file : {:?}", e); }
            }
        }
    
        pub fn get(&self, key: K) -> Option<&V> {
            self.range_map.get(&key)
        }
    }

Cargo complains:

error[E0106]: missing lifetime specifier
  --> src\main.rs:13:61
   |
13 |         K: Ord + Clone + Eq + Debug + Default + Serialize + Deserialize,
   |                                                             ^^^^^^^^^^^ expected named lifetime parameter
   |
   = note: for more information on higher-ranked polymorphism, visit https://doc.rust-lang.org/nomicon/hrtb.html
help: consider making the bound lifetime-generic with a new `'a` lifetime
   |
13 |         K: Ord + Clone + Eq + Debug + Default + Serialize + for<'a> Deserialize<'a>,
   |                                                             +++++++            ++++
help: consider making the bound lifetime-generic with a new `'a` lifetime
   |
13 |         for<'a> K: Ord + Clone + Eq + Debug + Default + Serialize + Deserialize<'a>,
   |         +++++++                                                                ++++
help: consider introducing a named lifetime parameter
   |
11 ~ pub struct RangeDataStorage<'a, K, V>
12 |     where
13 ~         K: Ord + Clone + Eq + Debug + Default + Serialize + Deserialize<'a>,

In this iteration, cargo complains that there are missing lifetime specifiers. I'm still not sure why this is a problem. I've read the serde deserializer lifetime article:

https://serde.rs/lifetimes.html

and I'm not sure what I'm missing. I'm figuring that the lifetimes are necessary because of serde's focus on zero-copy deserialization (the deserialized object refers to the deserializer's borrowed data, so the borrow checker needs to ensure that the borrowed data doesn't fall out of scope, thus the lifetimes).

However, cloning the data should fix this problem, right? It doesn't.

Cargo recommends adding higher-rank trait bounds for the missing lifetime specifiers; the aforementioned serde article mentions that the higher-rank trait bounds are equivalent to using DeserializeOwned (I've tried both):

Adding the higher-rank trait bounds:

    #[derive(Debug, Clone, Serialize, Deserialize)]
    pub struct RangeDataStorage<K, V>
        where
            K: Ord + Clone + Eq + Debug + Default + Serialize + for<'a>Deserialize<'a>,
            V: Default + Serialize + Debug + Eq + Clone + for<'a>Deserialize<'a> {
        range_map: RangeMap<K, V>,
    }
    
    impl<K, V> RangeDataStorage<K, V>
        where
            K: Ord + Clone + Eq + Debug + Default + Serialize + for<'a>Deserialize<'a>,
            V: Default + Serialize + Debug + Eq + Clone + for<'a>Deserialize<'a>{

New issue, type annotations needed? Where? How?

    error[E0283]: type annotations needed: cannot satisfy `K: Deserialize<'_>`
      --> src\main.rs:11:12
       |
    11 | pub struct RangeDataStorage<K, V>
       |            ^^^^^^^^^^^^^^^^^^^^^^
       |
    note: multiple `impl`s or `where` clauses satisfying `K: Deserialize<'_>` found
      --> src\main.rs:10:35
       |
    10 | #[derive(Debug, Clone, Serialize, Deserialize)]
       |                                   ^^^^^^^^^^^
    ...
    13 |         K: Ord + Clone + Eq + Debug + Default + Serialize + for<'a>Deserialize<'a>,
       |                                                             ^^^^^^^^^^^^^^^^^^^^^^
    note: required for `RangeDataStorage<K, V>` to implement `Deserialize<'de>`
      --> src\main.rs:10:35
       |
    10 | #[derive(Debug, Clone, Serialize, Deserialize)]
       |                                   ^^^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro
    11 | pub struct RangeDataStorage<K, V>
       |            ^^^^^^^^^^^^^^^^^^^^^^
       = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)

The same error occurs when replacing the higher rank trait bounds with DeserializeOwned:

    #[derive(Debug, Clone, Serialize, Deserialize)]
    pub struct RangeDataStorage<K, V>
        where
            K: Ord + Clone + Eq + Debug + Default + Serialize + DeserializeOwned,
            V: Default + Serialize + Debug + Eq + Clone + DeserializeOwned {
        range_map: RangeMap<K, V>,
    }
    
    impl<K, V> RangeDataStorage<K, V>
        where
            K: Ord + Clone + Eq + Debug + Default + Serialize + DeserializeOwned,
            V: Default + Serialize + Debug + Eq + Clone + DeserializeOwned{
    error[E0283]: type annotations needed: cannot satisfy `K: Deserialize<'_>`
      --> src\main.rs:11:12
       |
    11 | pub struct RangeDataStorage<K, V>
       |            ^^^^^^^^^^^^^^^^^^^^^^
       |
    note: multiple `impl`s or `where` clauses satisfying `K: Deserialize<'_>` found
      --> src\main.rs:10:35
       |
    10 | #[derive(Debug, Clone, Serialize, Deserialize)]
       |                                   ^^^^^^^^^^^
    ...
    13 |         K: Ord + Clone + Eq + Debug + Default + Serialize + DeserializeOwned,
       |                                                             ^^^^^^^^^^^^^^^^
    note: required for `RangeDataStorage<K, V>` to implement `Deserialize<'de>`
      --> src\main.rs:10:35
       |
    10 | #[derive(Debug, Clone, Serialize, Deserialize)]
       |                                   ^^^^^^^^^^^ unsatisfied trait bound introduced in this `derive` macro
    11 | pub struct RangeDataStorage<K, V>
       |            ^^^^^^^^^^^^^^^^^^^^^^
       = note: this error originates in the derive macro `Deserialize` (in Nightly builds, run with -Z macro-backtrace for more info)

A brief google search on this problem gives the following result: https://github.com/serde-rs/serde/issues/2418

This doesn't help me, since removing the where clause on my struct also causes me problems, in that cargo now complains that types K and V need to be bounded by Ord, Clone, Eq (necessitated by Deserialize).

At this point I'm lost, and I can't really find any more information for a task that should be relatively simple, and I haven't even gotten to implementing the Drop function.

Thanks for any help you can give.


Solution

  • The Deserialize derive macro can't figure out what the actual bounds needed to implement Deserialize are for your struct. The result is the rather opaque error messages you get from the compiler.

    To fix this, you can write the Deserialize implementation bounds yourself instead of having serde try to figure them out. This also means you can drop the where bound from your struct entirely; you don't actually need any of those bounds to define the struct, and usually you shouldn't bound the generic parameters of a type unless it's absolutely required to define the type.

    The result looks something like this:

    #[derive(Debug, Clone, Serialize, Deserialize)]
    pub struct RangeDataStorage<K, V> {
        #[serde(bound(deserialize = r"
            K: Ord + Clone + Deserialize<'de>,
            V: Eq + Clone + Deserialize<'de>,
        "))]
        range_map: RangeMap<K, V>,
    }
    

    You can derive these bounds either by starting with empty bounds and adding what the compiler suggests until it compiles, or by copying the bounds from RangeMap's Deserialize implementation.