150 lines
6.2 KiB
Markdown
150 lines
6.2 KiB
Markdown
Erased Serde
|
|
============
|
|
|
|
[<img alt="github" src="https://img.shields.io/badge/github-dtolnay/erased--serde-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/dtolnay/erased-serde)
|
|
[<img alt="crates.io" src="https://img.shields.io/crates/v/erased-serde.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/erased-serde)
|
|
[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-erased--serde-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/erased-serde)
|
|
[<img alt="build status" src="https://img.shields.io/github/actions/workflow/status/dtolnay/erased-serde/ci.yml?branch=master&style=for-the-badge" height="20">](https://github.com/dtolnay/erased-serde/actions?query=branch%3Amaster)
|
|
|
|
This crate provides type-erased versions of Serde's `Serialize`, `Serializer`
|
|
and `Deserializer` traits that can be used as [trait objects].
|
|
|
|
[trait objects]: https://doc.rust-lang.org/book/first-edition/trait-objects.html
|
|
|
|
- [`erased_serde::Serialize`](https://docs.rs/erased-serde/0.4/erased_serde/trait.Serialize.html)
|
|
- [`erased_serde::Serializer`](https://docs.rs/erased-serde/0.4/erased_serde/trait.Serializer.html)
|
|
- [`erased_serde::Deserializer`](https://docs.rs/erased-serde/0.4/erased_serde/trait.Deserializer.html)
|
|
|
|
The usual Serde `Serialize`, `Serializer` and `Deserializer` traits cannot be
|
|
used as trait objects like `&dyn Serialize` or boxed trait objects like
|
|
`Box<dyn Serialize>` because of Rust's ["object safety" rules]. In particular,
|
|
all three traits contain generic methods which cannot be made into a trait
|
|
object.
|
|
|
|
["object safety" rules]: http://huonw.github.io/blog/2015/01/object-safety/
|
|
|
|
This library should be considered a low-level building block for interacting
|
|
with Serde APIs in an object-safe way. Most use cases will require higher level
|
|
functionality such as provided by [`typetag`] which uses this crate internally.
|
|
|
|
[`typetag`]: https://github.com/dtolnay/typetag
|
|
|
|
**The traits in this crate work seamlessly with any existing Serde `Serialize`
|
|
and `Deserialize` type and any existing Serde `Serializer` and `Deserializer`
|
|
format.**
|
|
|
|
```toml
|
|
[dependencies]
|
|
serde = "1.0"
|
|
erased-serde = "0.4"
|
|
```
|
|
|
|
## Serialization
|
|
|
|
```rust
|
|
use erased_serde::{Serialize, Serializer};
|
|
use std::collections::BTreeMap as Map;
|
|
use std::io;
|
|
|
|
fn main() {
|
|
// Construct some serializers.
|
|
let json = &mut serde_json::Serializer::new(io::stdout());
|
|
let cbor = &mut serde_cbor::Serializer::new(serde_cbor::ser::IoWrite::new(io::stdout()));
|
|
|
|
// The values in this map are boxed trait objects. Ordinarily this would not
|
|
// be possible with serde::Serializer because of object safety, but type
|
|
// erasure makes it possible with erased_serde::Serializer.
|
|
let mut formats: Map<&str, Box<dyn Serializer>> = Map::new();
|
|
formats.insert("json", Box::new(<dyn Serializer>::erase(json)));
|
|
formats.insert("cbor", Box::new(<dyn Serializer>::erase(cbor)));
|
|
|
|
// These are boxed trait objects as well. Same thing here - type erasure
|
|
// makes this possible.
|
|
let mut values: Map<&str, Box<dyn Serialize>> = Map::new();
|
|
values.insert("vec", Box::new(vec!["a", "b"]));
|
|
values.insert("int", Box::new(65536));
|
|
|
|
// Pick a Serializer out of the formats map.
|
|
let format = formats.get_mut("json").unwrap();
|
|
|
|
// Pick a Serialize out of the values map.
|
|
let value = values.get("vec").unwrap();
|
|
|
|
// This line prints `["a","b"]` to stdout.
|
|
value.erased_serialize(format).unwrap();
|
|
}
|
|
```
|
|
|
|
## Deserialization
|
|
|
|
```rust
|
|
use erased_serde::Deserializer;
|
|
use std::collections::BTreeMap as Map;
|
|
|
|
fn main() {
|
|
static JSON: &[u8] = br#"{"A": 65, "B": 66}"#;
|
|
static CBOR: &[u8] = &[162, 97, 65, 24, 65, 97, 66, 24, 66];
|
|
|
|
// Construct some deserializers.
|
|
let json = &mut serde_json::Deserializer::from_slice(JSON);
|
|
let cbor = &mut serde_cbor::Deserializer::from_slice(CBOR);
|
|
|
|
// The values in this map are boxed trait objects, which is not possible
|
|
// with the normal serde::Deserializer because of object safety.
|
|
let mut formats: Map<&str, Box<dyn Deserializer>> = Map::new();
|
|
formats.insert("json", Box::new(<dyn Deserializer>::erase(json)));
|
|
formats.insert("cbor", Box::new(<dyn Deserializer>::erase(cbor)));
|
|
|
|
// Pick a Deserializer out of the formats map.
|
|
let format = formats.get_mut("json").unwrap();
|
|
|
|
let data: Map<String, usize> = erased_serde::deserialize(format).unwrap();
|
|
|
|
println!("{}", data["A"] + data["B"]);
|
|
}
|
|
```
|
|
|
|
## How it works
|
|
|
|
This crate is based on a general technique for building trait objects of traits
|
|
that have generic methods (like all of Serde's traits). [This example code]
|
|
demonstrates the technique applied to a simplified case of a single generic
|
|
method. [Try it in the playground.]
|
|
|
|
[This example code]: https://github.com/dtolnay/erased-serde/blob/master/explanation/main.rs
|
|
[Try it in the playground.]: https://play.rust-lang.org/?gist=c1111875e7462ba3d0190aacb2fc2211
|
|
|
|
In erased-serde things are a bit more complicated than in the example for three
|
|
reasons but the idea is the same.
|
|
|
|
- We need to deal with trait methods that take `self` by value -- effectively by
|
|
implementing the object-safe trait for `Option<T>` where `T` implements the
|
|
real trait.
|
|
- We need to deal with traits that have associated types like `Serializer::Ok`
|
|
and `Visitor::Value` -- by carefully short-term stashing things behind a
|
|
pointer.
|
|
- We need to support trait methods that have a generic type in the return type
|
|
but none of the argument types, like `SeqAccess::next_element` -- this can be
|
|
flipped around into a callback style where the return value is instead passed
|
|
on to a generic argument.
|
|
|
|
In the future maybe the Rust compiler will be able to apply this technique
|
|
automatically to any trait that is not already object safe by the current rules.
|
|
|
|
<br>
|
|
|
|
#### License
|
|
|
|
<sup>
|
|
Licensed under either of <a href="LICENSE-APACHE">Apache License, Version
|
|
2.0</a> or <a href="LICENSE-MIT">MIT license</a> at your option.
|
|
</sup>
|
|
|
|
<br>
|
|
|
|
<sub>
|
|
Unless you explicitly state otherwise, any contribution intentionally submitted
|
|
for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
|
|
be dual licensed as above, without any additional terms or conditions.
|
|
</sub>
|