Search code examples
rustserde

implementation of Serde::Deserialize is not general enough


I'm using event_emmiter_rs for event handling in my app. This library allows you to subscribe to events with callbacks and also fire those events. Events are in the form of (strings, value) and callbacks are in the form of closures that take in a value parameter. Values sent through the event callbacks must implement Serde::Deserialize. We can see this here in the docs. So I created this simple setup:

use event_emitter_rs::EventEmitter;
use serde::Serialize;
use serde::Deserialize;
use std::borrow::Cow;

#[derive(Clone, Serialize, Deserialize, Debug)]
#[serde(bound(deserialize = "'de: 'static"))]
//#[serde(bound(deserialize = "'de: 'a"))] using the 'a lifetime gives same error
pub struct DraggableInfo<'a>{
    parent: WidgetValue<'a>,
    index: WidgetValue<'a>,
    draggable_id: WidgetValue<'a>,
}

impl<'a> DraggableInfo<'a>{
    pub fn new(parent: &'static str, index: u32, draggable_id: &'static str)->Self{
        DraggableInfo{
            parent: WidgetValue::CString(Cow::Borrowed(parent)),
            index: WidgetValue::Unsized32(index),
            draggable_id: WidgetValue::CString(Cow::Borrowed(draggable_id)),
        }
    }
}

#[derive(Clone, Serialize, Deserialize, Debug)]
pub enum WidgetValue<'a>{
    Integer32(i32),
    Unsized32(u32),
    CString(Cow<'a, str>)
}

fn main(){
    let mut event_emitter = EventEmitter::new();
    event_emitter.on("Print Draggable Value", |dragValue: DraggableInfo| {dbg!(dragValue);});
    event_emitter.emit("Print Draggable Value", DraggableInfo::new("root", 0, "layer 1")); 
}

But this results in error message:

error: implementation of `Deserialize` is not general enough
  --> src\main.rs:34:19
   |
34 |     event_emitter.on("Print Draggable Value", |dragValue: DraggableInfo| {dbg!(dragValue);});
   |                   ^^ implementation of `Deserialize` is not general enough
   |
   = note: `DraggableInfo<'_>` must implement `Deserialize<'0>`, for any lifetime `'0`...
   = note: ...but `DraggableInfo<'_>` actually implements `Deserialize<'1>`, for some specific lifetime `'1`

I'm not sure what the Deserialize<'0> and Deserialize<'1> lifetimes the message refers to are, or exactly what the compiler means when it says the impl is "too general". How might I fix this error?


Solution

  • Question dismissed in comments, but your specific problem was prominent in this line:

    #[serde(bound(deserialize = "'de: 'static"))]

    As Serde guide forewarns:

    Note that <T> where T: Deserialize<'static> is never what you want. Also Deserialize<'de> + 'static is never what you want. Generally writing 'static anywhere near Deserialize is a sign of being on the wrong track. Use one of the above bounds instead.

    Which should make practical sense: you'll never want to deserialize from 100% static data. Those WidgetValues you have, they'll go in and out of scope (be created/destroyed) dynamically at runtime, right?..

    But when you define the CString variant with a reference into the raw input buffer (the event payload from which deserialization happens) — you have to ensure that not a single WidgetValue ever outlives its input buffer. That's what Rust lifetimes are for, they encode machine-checked guarantees that Bad Things™ won't happen.

    From comments again: the simple solution is to own the data instead of borrowing (referencing) it, i.e.

    pub enum WidgetValue {
        Integer32(i32),
        Unsized32(u32),
        CString(String),
    }
    

    ... but this simplicity will make you pay a performance cost, in heap allocations and gratuitous string copying whenever WidgetValues are passed around. You'll lose the zero-copy capacity supported by Serde. Not saying it's inherently bad; perhaps that price is okay for your application.

    However, many programmers pick Rust for those applications where performance matters. Without compromise on safety, too. Which is to say: let's get our hands dirty.


    Note: Unsized32 is an oxymoron, you probably meant to write Unsigned32.


    Note: in DraggableInfo::new signature, why require the borrowed string slices to have 'static lifetime? That's way too strict. Simply 'a will suffice.


    the error is clear that the type needs to implement Deserialize for any lifetime

    Indeed; the .on signature does not have the 'de as generic parameter:

    pub fn on<F, T>(&mut self, event: &str, callback: F) -> String where
        T: Deserialize<'de>,
        F: Fn(T) + 'static + Sync + Send, 
    

    Which implies a caller doesn't get to choose the lifetime. There's a chance that this is unintended, perhaps try asking the author or event_emitter_rs library; could be a subtle mistake, could be by design.

    BTW: if you want to use things like serde_json::from_reader for deserializing truly dynamically in a stream — this whole zero-copy endeavor will not work out. That's expressed in that DeserializeOwned constraint. This fact is also mentioned in the guide, For example when deserializing from an IO stream no data can be borrowed.

    At this point, I'm abandoning further research, because am visiting this question in the context of parsing JSON from a stream. This means I have no choice but to do DeserializeOwned (i.e. use owned String's in my data structures instead of &'a str). Which also makes sense, because stream data is ephemeral, so borrowing from it simply won't work.