How to deserialize a struct from json which contains &'static str.?


use serde::{Deserialize, Serialize};

fn deserialize_static_str<'de, D>(deserializer: D) -> Result<&'static str, D::Error>
    where
        D: serde::Deserializer<'de>,
{
    let s: String = Deserialize::deserialize(deserializer)?;
    Ok(Box::leak(s.into_boxed_str()))
}


#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Params {    
    pub open_vol: i32,
    pub open_time: f64,
    pub close_time: f64,
    pub price_tick: f64,
    pub size: f64,
    #[serde(deserialize_with = "deserialize_static_str")]
    pub code: &'static str,

    #[serde(deserialize_with = "deserialize_static_str")]
    pub contract: &'static str,
    pub night: bool,
    pub night_time: (u8, u8),

    #[serde(deserialize_with = "deserialize_static_str")]
    pub base_path: &'static str,

    #[serde(deserialize_with = "deserialize_static_str")]
    pub next_code: &'static str,
    pub arb_l1: f64,
    pub arb_l2: f64,
    pub arb_l3: f64,
    pub arb_l4: f64,
}

and use

    pub fn parse_from_file(file_path: impl Into<PathBuf>) -> Params {
        let file = File::open(file_path.into()).unwrap();
        let reader = BufReader::new(file);
        let params: Params = serde_json::from_reader(reader).unwrap();
        params
    }

but got

  --> src/params.rs:74:30
   |
74 |         let params: Params = serde_json::from_reader(reader).unwrap();
   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Deserialize` is not general enough
   |
   = note: `Params` must implement `Deserialize<'0>`, for any lifetime `'0`...
   = note: ...but it actually implements `Deserialize<'static>`

so how can fix the problem ? \

hey, i don't want to use String.

In general, it is not possible to deserialize into a &'static str.

If you have a &'static str to be parsed to begin with, and the serialization format doesn't use escaping, then it might be possible. But if you are trying to deserialize from a reader, then it's simply not possible (the underlying value has to be stored somewhere).

1 Like

but why not Box::leak do not work . we can use parse to String first. and use Box::leak to &'static str

Speaking as someone who's recently gained a lot of experience with Box::leak() (it does have valid use cases):

You could do that, and then you'd have yourself a fine memory leak that's next to impossible to fix.

In this case the better option is to change the Params type — i.e. the one that contains the &'static str field — or, if that's not possible, to create a fit-for-purpose set of types to which you can easily deserialize, starting with ParamsOwned which has the same fields as Params except that instead of &'static str fields it has String fields.

1 Like

If you are willing to leak memory, then it can work. I'm not sure what code gives you that error, because what you posted compiles.

see here ...

Use Cow<'static, str> instead of &'static str if you really need to assign &'static str to it sometime. Decoding will use String, which it can properly free. Using heap-allocated and leaked string does not give you any performance advantage over using heap-allocated and not leaked String.

&'static str makes sense only for predefined strings hardcoded into the program. But if your field can have only one of predefined strings, then don't even bother with str, and use an enum instead. This is easy to deserialize (serde will match enum fields by name) and efficient to store.

4 Likes

The derive macro detects fields with the shape &'a str and automatically borrows. The resulting trait implementation will them be Deserialize<'de> where 'de: 'a or in the special case of 'static it is Deserialize<'static>.
This makes it incompatible with DeserializeOwned as used by from_reader.

You can trick the derive macro by hiding the fact that you have a &'static str.

type StaticStr = &'static str;

#[derive(Debug, Clone, Deserialize, Serialize)]
pub struct Params {    
    #[serde(deserialize_with = "deserialize_static_str")]
    pub code: StaticStr,
}

Alternatively, create the Deserializer with serde_json::Deserializer::from_reader and call Params::deserialize(deserializer) instead of using from_reader.

..... got it......................

That's not really a solution, though, and there's no good reason for using it. You should just follow @kornel's suggestion. Your approach based on leaking is not better than any alternative (it's worse than the Cow in that it leaks memory and abuses the type system by trying to shove owned data in a borrow-shaped hole, in particular).

3 Likes

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.