I ran into a case where I was looking to serialize a struct that contained a field of type std::time::Instant.
This is currently not supported by serde
so I thought I would take the opportunity and try to add an implementation for it. I looked at https://github.com/serde-rs/serde/pull/476 and https://github.com/serde-rs/serde/pull/949 as starting points.
I think the main issue with an Instant
implementation stems from the following:
Instants are opaque types that can only be compared to one another. There is no method to get "the number of seconds" from an instant. Instead, it only allows measuring the duration between two instants (or comparing two instants).
Duration
is composed of a whole number of seconds and a fractional part represented in nanoseconds. SystemTime
is able to anchor off of UNIX_EPOCH. Therefore, there are ways to obtain the underlying data representing these structs.
Since there is nothing similar for Instant
, and it can only be compared with other Instant
s, is an implementation for this currently a dead end? I would love to be able to tackle this, but I am not sure if serialization is possible since there is no way to obtain the underlying data representation.
If there is no way to obtain some form of serializable underlying data from a type, then there is no way it can be serialized. Or in the reverse situation if there is no way to instantiate a type from some deserializable underlying data, it isn't possible to deserialize the type.
In this case it looks like Instant
is carefully designed to expose only what is necessary for the use case described in the docs, a monotonically nondecreasing clock that can be compared relative to other instants. I am closing this issue because the API is not sufficient to enable Instant
to be serialized or deserialized.
As a hacky approximate workaround you could serialize an approximation of an Instant
in the following way. Note that this provides no upper bound on how wrong the value might be after serializing and deserializing. It might be within a few microseconds of the original value most of the time but wrong by several hours other times, especially if the system time skips around.
(The real fix is still to use a different type.)
#[macro_use]
extern crate serde_derive;
extern crate serde;
extern crate serde_json;
use std::time::Instant;
#[derive(Serialize, Deserialize, Debug)]
struct S {
#[serde(with = "approx_instant")]
time: Instant,
}
mod approx_instant {
use std::time::{Instant, SystemTime};
use serde::{Serialize, Serializer, Deserialize, Deserializer, de::Error};
pub fn serialize<S>(instant: &Instant, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let system_now = SystemTime::now();
let instant_now = Instant::now();
let approx = system_now - (instant_now - *instant);
approx.serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Instant, D::Error>
where
D: Deserializer<'de>,
{
let de = SystemTime::deserialize(deserializer)?;
let system_now = SystemTime::now();
let instant_now = Instant::now();
let duration = system_now.duration_since(de).map_err(Error::custom)?;
let approx = instant_now - duration;
Ok(approx)
}
}
fn main() {
let s = S { time: Instant::now() };
println!("before: {:?}\n", s);
let j = serde_json::to_string(&s).unwrap();
println!("json: {}\n", j);
let s: S = serde_json::from_str(&j).unwrap();
println!("after: {:?}\n", s);
}
If you are only interested by elapsed time, you can serialize and deserialize like this :
use std::time::{Duration, Instant};
use serde::{Serialize, Serializer, Deserialize, Deserializer};
use serde::de::Error;
pub fn serialize<S>(instant: &Instant, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let duration = instant.elapsed();
duration.serialize(serializer)
}
pub fn deserialize<'de, D>(deserializer: D) -> Result<Instant, D::Error>
where
D: Deserializer<'de>,
{
let duration = Duration::deserialize(deserializer)?;
let now = Instant::now();
let instant = now.checked_sub(duration).ok_or_else(|| Error::custom("Erreur checked_add"))?;
Ok(instant)
}
Most helpful comment
As a hacky approximate workaround you could serialize an approximation of an
Instant
in the following way. Note that this provides no upper bound on how wrong the value might be after serializing and deserializing. It might be within a few microseconds of the original value most of the time but wrong by several hours other times, especially if the system time skips around.(The real fix is still to use a different type.)