Skip to content

Commit

Permalink
Merge pull request #25 from alecmocatta/conversion-methods
Browse files Browse the repository at this point in the history
Conversion methods
  • Loading branch information
mergify[bot] authored Nov 24, 2019
2 parents 494d1fb + 3fad4c6 commit d1a59ff
Show file tree
Hide file tree
Showing 7 changed files with 62 additions and 22 deletions.
5 changes: 5 additions & 0 deletions .editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
[*]
tab_width = 4

[.*]
tab_width = 4
5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "serde_traitobject"
version = "0.2.3"
version = "0.2.4"
license = "MIT OR Apache-2.0"
authors = ["Alec Mocatta <[email protected]>"]
categories = ["development-tools","encoding","rust-patterns","network-programming"]
Expand All @@ -12,7 +12,7 @@ This library enables the serialization and deserialization of trait objects such
"""
repository = "https://github.com/alecmocatta/serde_traitobject"
homepage = "https://github.com/alecmocatta/serde_traitobject"
documentation = "https://docs.rs/serde_traitobject/0.2.3"
documentation = "https://docs.rs/serde_traitobject/0.2.4"
readme = "README.md"
edition = "2018"

Expand All @@ -31,6 +31,7 @@ serde_derive = "1.0"
serde_json = "1.0"
bincode = "1.0"
serde_closure = "0.2.2"
wasm-bindgen-test = "0.3"

[[test]]
name = "test"
Expand Down
27 changes: 16 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,39 @@
[![MIT / Apache 2.0 licensed](https://img.shields.io/crates/l/serde_traitobject.svg?maxAge=2592000)](#License)
[![Build Status](https://dev.azure.com/alecmocatta/serde_traitobject/_apis/build/status/tests?branchName=master)](https://dev.azure.com/alecmocatta/serde_traitobject/_build/latest?branchName=master)

[Docs](https://docs.rs/serde_traitobject/0.2.3/serde_traitobject/)
[Docs](https://docs.rs/serde_traitobject/0.2.4/serde_traitobject/)

**Serializable and deserializable trait objects.**

This library enables the serialization and deserialization of trait objects so they can be sent between other processes running the same binary.

For example, if you have multiple forks of a process, or the same binary running on each of a cluster of machines, this library lets you send trait objects between them.

Any trait can be made (de)serializable when made into a trait object by adding this crate's [Serialize](https://docs.rs/serde_traitobject/0.2.3/serde_traitobject/trait.Serialize.html) and [Deserialize](https://docs.rs/serde_traitobject/0.2.3/serde_traitobject/trait.Deserialize.html) traits as supertraits:
Any trait can be made (de)serializable when made into a trait object by adding this crate's [Serialize](https://docs.rs/serde_traitobject/0.2.4/serde_traitobject/trait.Serialize.html) and [Deserialize](https://docs.rs/serde_traitobject/0.2.4/serde_traitobject/trait.Deserialize.html) traits as supertraits:

```rust
trait MyTrait: serde_traitobject::Serialize + serde_traitobject::Deserialize {
fn my_method(&self);
fn my_method(&self);
}

#[derive(Serialize, Deserialize)]
struct Message(#[serde(with = "serde_traitobject")] Box<dyn MyTrait>);
struct Message {
#[serde(with = "serde_traitobject")]
message: Box<dyn MyTrait>,
}

// Woohoo, `Message` is now serializable!
```

And that's it! The two traits are automatically implemented for all `T: serde::Serialize` and all `T: serde::de::DeserializeOwned`, so as long as all implementors of your trait are themselves serializable then you're good to go.

There are two ways to (de)serialize your trait object:
* Apply the `#[serde(with = "serde_traitobject")]` [field attribute](https://serde.rs/attributes.html), which instructs serde to use this crate's [serialize](https://docs.rs/serde_traitobject/0.2.3/serde_traitobject/fn.serialize.html) and [deserialize](https://docs.rs/serde_traitobject/0.2.3/serde_traitobject/fn.deserialize.html) functions;
* The [Box](https://docs.rs/serde_traitobject/0.2.3/serde_traitobject/struct.Box.html), [Rc](https://docs.rs/serde_traitobject/0.2.3/serde_traitobject/struct.Rc.html) and [Arc](https://docs.rs/serde_traitobject/0.2.3/serde_traitobject/struct.Arc.html) structs, which are simple wrappers around their stdlib counterparts that automatically handle (de)serialization without needing the above annotation;
* Apply the `#[serde(with = "serde_traitobject")]` [field attribute](https://serde.rs/attributes.html), which instructs serde to use this crate's [serialize](https://docs.rs/serde_traitobject/0.2.4/serde_traitobject/fn.serialize.html) and [deserialize](https://docs.rs/serde_traitobject/0.2.4/serde_traitobject/fn.deserialize.html) functions;
* The [Box](https://docs.rs/serde_traitobject/0.2.4/serde_traitobject/struct.Box.html), [Rc](https://docs.rs/serde_traitobject/0.2.4/serde_traitobject/struct.Rc.html) and [Arc](https://docs.rs/serde_traitobject/0.2.4/serde_traitobject/struct.Arc.html) structs, which are simple wrappers around their stdlib counterparts that automatically handle (de)serialization without needing the above annotation;

Additionally, there are several convenience traits implemented that extend their stdlib counterparts:

* [Any](https://docs.rs/serde_traitobject/0.2.3/serde_traitobject/trait.Any.html), [Debug](https://docs.rs/serde_traitobject/0.2.3/serde_traitobject/trait.Debug.html), [Display](https://docs.rs/serde_traitobject/0.2.3/serde_traitobject/trait.Display.html), [Error](https://docs.rs/serde_traitobject/0.2.3/serde_traitobject/trait.Error.html), [Fn](https://docs.rs/serde_traitobject/0.2.3/serde_traitobject/trait.Fn.html), [FnMut](https://docs.rs/serde_traitobject/0.2.3/serde_traitobject/trait.FnMut.html), [FnOnce](https://docs.rs/serde_traitobject/0.2.3/serde_traitobject/trait.FnOnce.html)
* [Any](https://docs.rs/serde_traitobject/0.2.4/serde_traitobject/trait.Any.html), [Debug](https://docs.rs/serde_traitobject/0.2.4/serde_traitobject/trait.Debug.html), [Display](https://docs.rs/serde_traitobject/0.2.4/serde_traitobject/trait.Display.html), [Error](https://docs.rs/serde_traitobject/0.2.4/serde_traitobject/trait.Error.html), [Fn](https://docs.rs/serde_traitobject/0.2.4/serde_traitobject/trait.Fn.html), [FnMut](https://docs.rs/serde_traitobject/0.2.4/serde_traitobject/trait.FnMut.html), [FnOnce](https://docs.rs/serde_traitobject/0.2.4/serde_traitobject/trait.FnOnce.html)

These are automatically implemented on all implementors of their stdlib counterparts that also implement `serde::Serialize` and `serde::de::DeserializeOwned`.

Expand All @@ -43,13 +46,13 @@ use serde_traitobject as s;

#[derive(Serialize, Deserialize, Debug)]
struct MyStruct {
foo: String,
bar: usize,
foo: String,
bar: usize,
}

let my_struct = MyStruct {
foo: String::from("abc"),
bar: 123,
foo: String::from("abc"),
bar: 123,
};

let erased: s::Box<dyn s::Any> = s::Box::new(my_struct);
Expand All @@ -69,6 +72,8 @@ This crate works by wrapping the vtable pointer with [`relative::Vtable`](https:

This approach is not yet secure against malicious actors. However, if we assume non-malicious actors and typical (static or dynamic) linking conditions, then it's not unreasonable to consider it sound.

See [here](https://github.com/rust-lang/rust/pull/66113) for ongoing work in `rustc` to make this safe and secure.

### Validation

Three things are serialized alongside the vtable pointer for the purpose of validation:
Expand Down
4 changes: 2 additions & 2 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ jobs:
endpoint: alecmocatta
default:
rust_toolchain: nightly
rust_lint_toolchain: nightly-2019-10-17
rust_lint_toolchain: nightly-2019-11-24
rust_flags: ''
rust_features: ''
rust_target_check: ''
Expand All @@ -29,4 +29,4 @@ jobs:
rust_target_run: 'x86_64-apple-darwin i686-apple-darwin'
linux:
imageName: 'ubuntu-16.04'
rust_target_run: 'x86_64-unknown-linux-gnu i686-unknown-linux-gnu x86_64-unknown-linux-musl i686-unknown-linux-musl'
rust_target_run: 'x86_64-unknown-linux-gnu i686-unknown-linux-gnu x86_64-unknown-linux-musl i686-unknown-linux-musl wasm32-unknown-unknown'
18 changes: 18 additions & 0 deletions src/convenience.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,24 @@ impl Box<dyn Any + Send + Sync> {
}
}
}
impl dyn Any + Send {
/// Convert into a `std::boxed::Box<dyn std::any::Any + Send>`.
pub fn into_any_send(self: boxed::Box<Self>) -> boxed::Box<dyn any::Any + Send> {
<Box<dyn Any + Send>>::into_any_send(Box(self))
}
}
impl dyn Any + Sync {
/// Convert into a `std::boxed::Box<dyn std::any::Any + Sync>`.
pub fn into_any_sync(self: boxed::Box<Self>) -> boxed::Box<dyn any::Any + Sync> {
<Box<dyn Any + Sync>>::into_any_sync(Box(self))
}
}
impl dyn Any + Send + Sync {
/// Convert into a `std::boxed::Box<dyn std::any::Any + Send + Sync>`.
pub fn into_any_send_sync(self: boxed::Box<Self>) -> boxed::Box<dyn any::Any + Send + Sync> {
<Box<dyn Any + Send + Sync>>::into_any_send_sync(Box(self))
}
}
impl<T: ?Sized + marker::Unsize<U>, U: ?Sized> ops::CoerceUnsized<Box<U>> for Box<T> {}
impl<T: ?Sized> Deref for Box<T> {
type Target = boxed::Box<T>;
Expand Down
19 changes: 12 additions & 7 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,14 @@
//! ```
//! # use serde_derive::{Serialize, Deserialize};
//! trait MyTrait: serde_traitobject::Serialize + serde_traitobject::Deserialize {
//! fn my_method(&self);
//! fn my_method(&self);
//! }
//!
//! #[derive(Serialize, Deserialize)]
//! struct Message(#[serde(with = "serde_traitobject")] Box<dyn MyTrait>);
//! struct Message {
//! #[serde(with = "serde_traitobject")]
//! message: Box<dyn MyTrait>,
//! }
//!
//! // Woohoo, `Message` is now serializable!
//! ```
Expand All @@ -39,13 +42,13 @@
//!
//! #[derive(Serialize, Deserialize, Debug)]
//! struct MyStruct {
//! foo: String,
//! bar: usize,
//! foo: String,
//! bar: usize,
//! }
//!
//! let my_struct = MyStruct {
//! foo: String::from("abc"),
//! bar: 123,
//! foo: String::from("abc"),
//! bar: 123,
//! };
//!
//! let erased: s::Box<dyn s::Any> = s::Box::new(my_struct);
Expand All @@ -66,6 +69,8 @@
//!
//! This approach is not yet secure against malicious actors. However, if we assume non-malicious actors and typical (static or dynamic) linking conditions, then it's not unreasonable to consider it sound.
//!
//! See [here](https://github.com/rust-lang/rust/pull/66113) for ongoing work in `rustc` to make this safe and secure.
//!
//! ## Validation
//!
//! Three things are serialized alongside the vtable pointer for the purpose of validation:
Expand Down Expand Up @@ -94,7 +99,7 @@
//!
//! This crate currently requires Rust nightly.
#![doc(html_root_url = "https://docs.rs/serde_traitobject/0.2.3")]
#![doc(html_root_url = "https://docs.rs/serde_traitobject/0.2.4")]
#![feature(
arbitrary_self_types,
coerce_unsized,
Expand Down
6 changes: 6 additions & 0 deletions tests/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ use serde_closure::Fn;
use serde_derive::{Deserialize, Serialize};
use serde_traitobject::{Deserialize, Serialize};
use std::{any, env, process, rc};
use wasm_bindgen_test::wasm_bindgen_test;

#[derive(Serialize, Deserialize)]
struct Abc {
Expand Down Expand Up @@ -87,6 +88,7 @@ impl<'a> AsRef<dyn Hello2Serialize + 'a> for dyn Hello2Serialize {
}
}

#[wasm_bindgen_test]
#[allow(clippy::too_many_lines)]
fn main() {
let test = |Abc {
Expand Down Expand Up @@ -223,6 +225,10 @@ fn main() {
o: vec![1u16, 2, 3].into(),
};

if cfg!(target_arch = "wasm32") {
return;
}

if let Ok(x) = env::var("SERDE_TRAITOBJECT_SPAWNED") {
let (a, bc): (_, Vec<u8>) = serde_json::from_str(&x).unwrap();
eq(&original, &a);
Expand Down

0 comments on commit d1a59ff

Please sign in to comment.