summaryrefslogtreecommitdiffstats
path: root/vendor/erased-serde/README.md
blob: 6061b319d92c1f67117f2c5e80326b750ec05db7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
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.3/erased_serde/trait.Serialize.html)
- [`erased_serde::Serializer`](https://docs.rs/erased-serde/0.3/erased_serde/trait.Serializer.html)
- [`erased_serde::Deserializer`](https://docs.rs/erased-serde/0.3/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.3"
```

## 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>