├── .editorconfig ├── .gitignore ├── .mergify.yml ├── .rustfmt.toml ├── Cargo.toml ├── LICENSE-APACHE.txt ├── LICENSE-MIT.txt ├── README.md ├── azure-pipelines.yml ├── src ├── convenience.rs └── lib.rs └── tests └── test.rs /.editorconfig: -------------------------------------------------------------------------------- 1 | [*] 2 | tab_width = 4 3 | 4 | [.*] 5 | tab_width = 4 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /.mergify.yml: -------------------------------------------------------------------------------- 1 | pull_request_rules: 2 | - name: automatic merge 3 | conditions: 4 | - base=master 5 | - status-success=tests 6 | - "label!=work in progress" 7 | - "#approved-reviews-by>=1" 8 | - "#review-requested=0" 9 | - "#changes-requested-reviews-by=0" 10 | actions: 11 | merge: 12 | method: merge 13 | strict: smart 14 | delete_head_branch: {} 15 | - name: automatic merge 16 | conditions: 17 | - base=master 18 | - status-success=tests 19 | - "label!=work in progress" 20 | - author=alecmocatta # https://github.com/Mergifyio/mergify-engine/issues/451 21 | - "#review-requested=0" 22 | - "#changes-requested-reviews-by=0" 23 | actions: 24 | merge: 25 | method: merge 26 | strict: smart 27 | delete_head_branch: {} 28 | - name: dismiss reviews 29 | conditions: [] 30 | actions: 31 | dismiss_reviews: {} 32 | - name: auto add wip label 33 | conditions: 34 | - "title~=^WIP: .*" 35 | actions: 36 | label: 37 | add: ["work in progress"] 38 | - name: auto remove wip label 39 | conditions: 40 | - "-title~=^WIP: .*" 41 | actions: 42 | label: 43 | remove: ["work in progress"] 44 | -------------------------------------------------------------------------------- /.rustfmt.toml: -------------------------------------------------------------------------------- 1 | hard_tabs = true 2 | imports_layout = "Horizontal" 3 | merge_imports = true 4 | fn_args_layout = "Compressed" 5 | use_field_init_shorthand = true 6 | 7 | # To enable when stable 8 | # wrap_comments = true # https://github.com/rust-lang/rustfmt/issues/3347 9 | # reorder_impl_items = true # https://github.com/rust-lang/rustfmt/issues/3363 10 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "serde_traitobject" 3 | version = "0.2.9" 4 | license = "MIT OR Apache-2.0" 5 | authors = ["Alec Mocatta "] 6 | categories = ["development-tools","encoding","rust-patterns","network-programming"] 7 | keywords = ["trait-object","serialization","serde","distributed"] 8 | description = """ 9 | Serializable and deserializable trait objects. 10 | 11 | This library enables the serialization and deserialization of trait objects such that they can be sent between other processes running the same binary. 12 | """ 13 | repository = "https://github.com/alecmocatta/serde_traitobject" 14 | homepage = "https://github.com/alecmocatta/serde_traitobject" 15 | documentation = "https://docs.rs/serde_traitobject" 16 | readme = "README.md" 17 | edition = "2018" 18 | 19 | [badges] 20 | azure-devops = { project = "alecmocatta/serde_traitobject", pipeline = "tests", build = "9" } 21 | maintenance = { status = "actively-developed" } 22 | 23 | [dependencies] 24 | serde = "1.0" 25 | erased-serde = "0.3" 26 | metatype = "0.2.2" 27 | 28 | relative = "0.2" 29 | serde_closure = { version = "0.3", optional = true } 30 | 31 | [dev-dependencies] 32 | bincode = "1.0" 33 | serde_closure = "0.3" 34 | serde_derive = "1.0" 35 | serde_json = "1.0" 36 | wasm-bindgen-test = "0.3" 37 | 38 | [[test]] 39 | name = "test" 40 | path = "tests/test.rs" 41 | harness = false 42 | -------------------------------------------------------------------------------- /LICENSE-APACHE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSE-MIT.txt: -------------------------------------------------------------------------------- 1 | Permission is hereby granted, free of charge, to any 2 | person obtaining a copy of this software and associated 3 | documentation files (the "Software"), to deal in the 4 | Software without restriction, including without 5 | limitation the rights to use, copy, modify, merge, 6 | publish, distribute, sublicense, and/or sell copies of 7 | the Software, and to permit persons to whom the Software 8 | is furnished to do so, subject to the following 9 | conditions: 10 | 11 | The above copyright notice and this permission notice 12 | shall be included in all copies or substantial portions 13 | of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 16 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 17 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 18 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 19 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 22 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 23 | DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # serde_traitobject 2 | 3 | [![Crates.io](https://img.shields.io/crates/v/serde_traitobject.svg?maxAge=86400)](https://crates.io/crates/serde_traitobject) 4 | [![MIT / Apache 2.0 licensed](https://img.shields.io/crates/l/serde_traitobject.svg?maxAge=2592000)](#License) 5 | [![Build Status](https://dev.azure.com/alecmocatta/serde_traitobject/_apis/build/status/tests?branchName=master)](https://dev.azure.com/alecmocatta/serde_traitobject/_build?definitionId=9) 6 | 7 | [📖 Docs](https://docs.rs/serde_traitobject) | [💬 Chat](https://constellation.zulipchat.com/#narrow/stream/213236-subprojects) 8 | 9 | **Serializable and deserializable trait objects.** 10 | 11 | This library enables the serialization and deserialization of trait objects so they can be sent between other processes running the same binary. 12 | 13 | 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. 14 | 15 | 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/serde_traitobject/trait.Serialize.html) and [Deserialize](https://docs.rs/serde_traitobject/0.2/serde_traitobject/trait.Deserialize.html) traits as supertraits: 16 | 17 | ```rust 18 | trait MyTrait: serde_traitobject::Serialize + serde_traitobject::Deserialize { 19 | fn my_method(&self); 20 | } 21 | 22 | #[derive(Serialize, Deserialize)] 23 | struct Message { 24 | #[serde(with = "serde_traitobject")] 25 | message: Box, 26 | } 27 | 28 | // Woohoo, `Message` is now serializable! 29 | ``` 30 | 31 | 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. 32 | 33 | There are two ways to (de)serialize your trait object: 34 | * 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/serde_traitobject/fn.serialize.html) and [deserialize](https://docs.rs/serde_traitobject/0.2/serde_traitobject/fn.deserialize.html) functions; 35 | * The [Box](https://docs.rs/serde_traitobject/0.2/serde_traitobject/struct.Box.html), [Rc](https://docs.rs/serde_traitobject/0.2/serde_traitobject/struct.Rc.html) and [Arc](https://docs.rs/serde_traitobject/0.2/serde_traitobject/struct.Arc.html) structs, which are simple wrappers around their stdlib counterparts that automatically handle (de)serialization without needing the above annotation; 36 | 37 | Additionally, there are several convenience traits implemented that extend their stdlib counterparts: 38 | 39 | * [Any](https://docs.rs/serde_traitobject/0.2/serde_traitobject/trait.Any.html), [Debug](https://docs.rs/serde_traitobject/0.2/serde_traitobject/trait.Debug.html), [Display](https://docs.rs/serde_traitobject/0.2/serde_traitobject/trait.Display.html), [Error](https://docs.rs/serde_traitobject/0.2/serde_traitobject/trait.Error.html), [Fn](https://docs.rs/serde_traitobject/0.2/serde_traitobject/trait.Fn.html), [FnMut](https://docs.rs/serde_traitobject/0.2/serde_traitobject/trait.FnMut.html), [FnOnce](https://docs.rs/serde_traitobject/0.2/serde_traitobject/trait.FnOnce.html) 40 | 41 | These are automatically implemented on all implementors of their stdlib counterparts that also implement `serde::Serialize` and `serde::de::DeserializeOwned`. 42 | 43 | ```rust 44 | use std::any::Any; 45 | use serde_traitobject as s; 46 | 47 | #[derive(Serialize, Deserialize, Debug)] 48 | struct MyStruct { 49 | foo: String, 50 | bar: usize, 51 | } 52 | 53 | let my_struct = MyStruct { 54 | foo: String::from("abc"), 55 | bar: 123, 56 | }; 57 | 58 | let erased: s::Box = s::Box::new(my_struct); 59 | 60 | let serialized = serde_json::to_string(&erased).unwrap(); 61 | let deserialized: s::Box = serde_json::from_str(&serialized).unwrap(); 62 | 63 | let downcast: Box = Box::::downcast(deserialized.into_any()).unwrap(); 64 | 65 | println!("{:?}", downcast); 66 | // MyStruct { foo: "abc", bar: 123 } 67 | ``` 68 | 69 | ## Security 70 | 71 | This crate works by wrapping the vtable pointer with [`relative::Vtable`](https://docs.rs/relative) such that it can safely be sent between processes. 72 | 73 | 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. 74 | 75 | See [here](https://github.com/rust-lang/rust/pull/66113) for ongoing work in `rustc` to make this safe and secure. 76 | 77 | ### Validation 78 | 79 | Three things are serialized alongside the vtable pointer for the purpose of validation: 80 | 81 | * the [`build_id`](https://github.com/alecmocatta/build_id) (128 bits); 82 | * the [`type_id`](https://doc.rust-lang.org/std/intrinsics/fn.type_id.html) of the trait object (64 bits); 83 | * the `type_id` of the concrete type (64 bits). 84 | 85 | At some point in Rust's future, I think it would be great if the latter could be used to safely look up and create a trait object. As it is, that functionality doesn't exist yet, so what this crate does instead is serialize the vtable pointer (relative to a static base), and do as much validity checking as it reasonably can before it can be used and potentially invoke UB. 86 | 87 | The first two are [checked for validity](https://github.com/alecmocatta/relative/blob/dae206663a09b9c0c4b3012c528b0e9c063df742/src/lib.rs#L457-L474) before usage of the vtable pointer. The `build_id` ensures that the vtable pointer came from an invocation of an identically laid out binary1. The `type_id` ensures that the trait object being deserialized is the same type as the trait object that was serialized. They ensure that under non-malicious conditions, attempts to deserialize invalid data return an error rather than UB. The `type_id` of the concrete type is used as a [sanity check](https://github.com/alecmocatta/serde_traitobject/blob/b20d74e183063e7d49aff2eabc9dcd5bc26d7c07/src/lib.rs#L469) that panics if it differs from the `type_id` of the concrete type to be deserialized. 88 | 89 | Regarding collisions, the 128 bit `build_id` colliding is sufficiently unlikely that it can be relied upon to never occur. The 64 bit `type_id` colliding is possible, see [rust-lang/rust#10389](https://github.com/rust-lang/rust/issues/10389), though exceedingly unlikely to occur in practise. 90 | 91 | The vtable pointer is (de)serialized as a usize relative to the vtable pointer of [this static trait object](https://github.com/alecmocatta/relative/blob/dae206663a09b9c0c4b3012c528b0e9c063df742/src/lib.rs#L90). This enables it to work under typical dynamic linking conditions, where the absolute vtable addresses can differ across invocations of the same binary, but relative addresses remain constant. 92 | 93 | All together this leaves, as far as I'm aware, three soundness holes: 94 | 95 | * A malicious user with a copy of the binary could trivially craft a `build_id` and `type_id` that pass validation and gives them control of where to jump to. 96 | * Data corruption of the serialized vtable pointer but not the `build_id` or `type_id` used for validation, resulting in a jump to an arbitrary address. This could be rectified in a future version of this library by using a cipher to make it vanishingly unlikely for corruptions to affect only the vtable pointer, by mixing the vtable pointer and validation components upon (de)serialization. 97 | * Dynamic linking conditions where the relative addresses (vtable - static vtable) are different across different invocations of the same binary. I'm sure this is possible, but it's not a scenario I've encountered so I can't speak to its commonness. 98 | 99 | 1I don't think this requirement is strictly necessary, as the `type_id` should include all information that could affect soundness (trait methods, calling conventions, etc), but it's included in case that doesn't hold in practise; to provide a more helpful error message; and to reduce the likelihood of collisions. 100 | 101 | ## Note 102 | 103 | This crate currently requires Rust nightly. 104 | 105 | ## License 106 | Licensed under either of 107 | 108 | * Apache License, Version 2.0, ([LICENSE-APACHE.txt](LICENSE-APACHE.txt) or http://www.apache.org/licenses/LICENSE-2.0) 109 | * MIT license ([LICENSE-MIT.txt](LICENSE-MIT.txt) or http://opensource.org/licenses/MIT) 110 | 111 | at your option. 112 | 113 | Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions. 114 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | trigger: ["master"] 2 | pr: ["master"] 3 | 4 | resources: 5 | repositories: 6 | - repository: templates 7 | type: github 8 | name: alecmocatta/azure-pipeline-templates 9 | endpoint: alecmocatta 10 | 11 | jobs: 12 | - template: rust.yml@templates 13 | parameters: 14 | endpoint: alecmocatta 15 | default: 16 | rust_toolchain: nightly 17 | rust_lint_toolchain: nightly-2022-11-23 18 | rust_flags: '' 19 | rust_features: ';serde_closure' 20 | rust_target_check: '' 21 | rust_target_build: '' 22 | rust_target_run: '' 23 | matrix: 24 | windows: 25 | imageName: 'windows-latest' 26 | rust_target_run: 'x86_64-pc-windows-msvc i686-pc-windows-msvc x86_64-pc-windows-gnu' 27 | mac: 28 | imageName: 'macos-latest' 29 | rust_target_run: 'x86_64-apple-darwin' 30 | linux: 31 | imageName: 'ubuntu-latest' 32 | rust_target_run: 'x86_64-unknown-linux-gnu i686-unknown-linux-gnu x86_64-unknown-linux-musl i686-unknown-linux-musl wasm32-unknown-unknown' 33 | -------------------------------------------------------------------------------- /src/convenience.rs: -------------------------------------------------------------------------------- 1 | use std::{ 2 | any, borrow::{Borrow, BorrowMut}, boxed, error, fmt, marker, marker::Tuple, ops::{self, Deref, DerefMut}, rc, sync 3 | }; 4 | 5 | use super::{deserialize, serialize, Deserialize, Serialize}; 6 | 7 | /// Convenience wrapper around [std::boxed::Box](std::boxed::Box) that automatically uses `serde_traitobject` for (de)serialization. 8 | #[derive(Clone, Default, PartialEq, Eq, Hash, PartialOrd, Ord)] 9 | pub struct Box(boxed::Box); 10 | impl Box { 11 | /// Create a new Box wrapper 12 | pub fn new(t: T) -> Self { 13 | Self(boxed::Box::new(t)) 14 | } 15 | } 16 | impl Box { 17 | /// Convert to a regular `std::Boxed::Box`. Coherence rules prevent currently prevent `impl Into> for Box`. 18 | pub fn into_box(self) -> boxed::Box { 19 | self.0 20 | } 21 | } 22 | impl Box { 23 | /// Convert into a `std::boxed::Box`. 24 | pub fn into_any(self) -> boxed::Box { 25 | self.0.into_any() 26 | } 27 | } 28 | impl Box { 29 | /// Convert into a `std::boxed::Box`. 30 | pub fn into_any_send(self) -> boxed::Box { 31 | unsafe { 32 | boxed::Box::from_raw(boxed::Box::into_raw(>::into_any(self)) as *mut _) 33 | } 34 | } 35 | } 36 | impl Box { 37 | /// Convert into a `std::boxed::Box`. 38 | pub fn into_any_sync(self) -> boxed::Box { 39 | unsafe { 40 | boxed::Box::from_raw(boxed::Box::into_raw(>::into_any(self)) as *mut _) 41 | } 42 | } 43 | } 44 | impl Box { 45 | /// Convert into a `std::boxed::Box`. 46 | pub fn into_any_send_sync(self) -> boxed::Box { 47 | unsafe { 48 | boxed::Box::from_raw(boxed::Box::into_raw(>::into_any(self)) as *mut _) 49 | } 50 | } 51 | } 52 | impl dyn Any + Send { 53 | /// Convert into a `std::boxed::Box`. 54 | pub fn into_any_send(self: boxed::Box) -> boxed::Box { 55 | >::into_any_send(Box(self)) 56 | } 57 | } 58 | impl dyn Any + Sync { 59 | /// Convert into a `std::boxed::Box`. 60 | pub fn into_any_sync(self: boxed::Box) -> boxed::Box { 61 | >::into_any_sync(Box(self)) 62 | } 63 | } 64 | impl dyn Any + Send + Sync { 65 | /// Convert into a `std::boxed::Box`. 66 | pub fn into_any_send_sync(self: boxed::Box) -> boxed::Box { 67 | >::into_any_send_sync(Box(self)) 68 | } 69 | } 70 | impl, U: ?Sized> ops::CoerceUnsized> for Box {} 71 | impl Deref for Box { 72 | type Target = boxed::Box; 73 | fn deref(&self) -> &Self::Target { 74 | &self.0 75 | } 76 | } 77 | impl DerefMut for Box { 78 | fn deref_mut(&mut self) -> &mut Self::Target { 79 | &mut self.0 80 | } 81 | } 82 | impl AsRef> for Box { 83 | fn as_ref(&self) -> &boxed::Box { 84 | &self.0 85 | } 86 | } 87 | impl AsMut> for Box { 88 | fn as_mut(&mut self) -> &mut boxed::Box { 89 | &mut self.0 90 | } 91 | } 92 | impl AsRef for Box { 93 | fn as_ref(&self) -> &T { 94 | &self.0 95 | } 96 | } 97 | impl AsMut for Box { 98 | fn as_mut(&mut self) -> &mut T { 99 | &mut self.0 100 | } 101 | } 102 | impl Borrow for Box { 103 | fn borrow(&self) -> &T { 104 | &self.0 105 | } 106 | } 107 | impl BorrowMut for Box { 108 | fn borrow_mut(&mut self) -> &mut T { 109 | &mut self.0 110 | } 111 | } 112 | impl From> for Box { 113 | fn from(t: boxed::Box) -> Self { 114 | Self(t) 115 | } 116 | } 117 | // impl Into> for Box { 118 | // fn into(self) -> boxed::Box { 119 | // self.0 120 | // } 121 | // } 122 | impl From for Box { 123 | fn from(t: T) -> Self { 124 | Self(boxed::Box::new(t)) 125 | } 126 | } 127 | impl error::Error for Box { 128 | #[allow(deprecated)] 129 | fn description(&self) -> &str { 130 | error::Error::description(&**self) 131 | } 132 | #[allow(deprecated)] 133 | fn cause(&self) -> Option<&dyn error::Error> { 134 | error::Error::cause(&**self) 135 | } 136 | fn source(&self) -> Option<&(dyn error::Error + 'static)> { 137 | error::Error::source(&**self) 138 | } 139 | } 140 | impl fmt::Debug for Box { 141 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 142 | self.0.fmt(f) 143 | } 144 | } 145 | impl fmt::Display for Box { 146 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 147 | self.0.fmt(f) 148 | } 149 | } 150 | impl ops::FnOnce for Box 151 | where 152 | F: FnOnce, 153 | { 154 | type Output = F::Output; 155 | extern "rust-call" fn call_once(self, args: A) -> Self::Output { 156 | self.0.call_once(args) 157 | } 158 | } 159 | impl ops::FnMut for Box 160 | where 161 | F: FnMut, 162 | { 163 | extern "rust-call" fn call_mut(&mut self, args: A) -> Self::Output { 164 | self.0.call_mut(args) 165 | } 166 | } 167 | impl ops::Fn for Box 168 | where 169 | F: Fn, 170 | { 171 | extern "rust-call" fn call(&self, args: A) -> Self::Output { 172 | self.0.call(args) 173 | } 174 | } 175 | impl serde::ser::Serialize for Box { 176 | fn serialize(&self, serializer: S) -> Result 177 | where 178 | S: serde::Serializer, 179 | { 180 | serialize(&self.0, serializer) 181 | } 182 | } 183 | impl<'de, T: Deserialize + ?Sized + 'static> serde::de::Deserialize<'de> for Box { 184 | fn deserialize(deserializer: D) -> Result 185 | where 186 | D: serde::Deserializer<'de>, 187 | { 188 | deserialize(deserializer).map(Self) 189 | } 190 | } 191 | 192 | /// Convenience wrapper around [std::rc::Rc](std::rc::Rc) that automatically uses `serde_traitobject` for (de)serialization. 193 | #[derive(Default, PartialEq, Eq, Hash, PartialOrd, Ord)] 194 | pub struct Rc(rc::Rc); 195 | impl Rc { 196 | /// Create a new Rc wrapper 197 | pub fn new(t: T) -> Self { 198 | Self(rc::Rc::new(t)) 199 | } 200 | } 201 | impl, U: ?Sized> ops::CoerceUnsized> for Rc {} 202 | impl Deref for Rc { 203 | type Target = rc::Rc; 204 | fn deref(&self) -> &Self::Target { 205 | &self.0 206 | } 207 | } 208 | impl DerefMut for Rc { 209 | fn deref_mut(&mut self) -> &mut Self::Target { 210 | &mut self.0 211 | } 212 | } 213 | impl AsRef> for Rc { 214 | fn as_ref(&self) -> &rc::Rc { 215 | &self.0 216 | } 217 | } 218 | impl AsMut> for Rc { 219 | fn as_mut(&mut self) -> &mut rc::Rc { 220 | &mut self.0 221 | } 222 | } 223 | impl AsRef for Rc { 224 | fn as_ref(&self) -> &T { 225 | &self.0 226 | } 227 | } 228 | impl Borrow for Rc { 229 | fn borrow(&self) -> &T { 230 | &self.0 231 | } 232 | } 233 | impl From> for Rc { 234 | fn from(t: rc::Rc) -> Self { 235 | Self(t) 236 | } 237 | } 238 | impl From> for rc::Rc { 239 | fn from(v: Rc) -> Self { 240 | v.0 241 | } 242 | } 243 | impl From for Rc { 244 | fn from(t: T) -> Self { 245 | Self(rc::Rc::new(t)) 246 | } 247 | } 248 | impl Clone for Rc { 249 | fn clone(&self) -> Self { 250 | Self(self.0.clone()) 251 | } 252 | } 253 | impl fmt::Debug for Rc { 254 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 255 | self.0.fmt(f) 256 | } 257 | } 258 | impl fmt::Display for Rc { 259 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 260 | self.0.fmt(f) 261 | } 262 | } 263 | impl serde::ser::Serialize for Rc { 264 | fn serialize(&self, serializer: S) -> Result 265 | where 266 | S: serde::Serializer, 267 | { 268 | serialize(&self.0, serializer) 269 | } 270 | } 271 | impl<'de, T: Deserialize + ?Sized + 'static> serde::de::Deserialize<'de> for Rc { 272 | fn deserialize(deserializer: D) -> Result 273 | where 274 | D: serde::Deserializer<'de>, 275 | { 276 | deserialize(deserializer).map(Self) 277 | } 278 | } 279 | 280 | /// Convenience wrapper around [std::sync::Arc](std::sync::Arc) that automatically uses `serde_traitobject` for (de)serialization. 281 | #[derive(Default, PartialEq, Eq, Hash, PartialOrd, Ord)] 282 | pub struct Arc(sync::Arc); 283 | impl Arc { 284 | /// Create a new Arc wrapper 285 | pub fn new(t: T) -> Self { 286 | Self(sync::Arc::new(t)) 287 | } 288 | } 289 | impl, U: ?Sized> ops::CoerceUnsized> for Arc {} 290 | impl Deref for Arc { 291 | type Target = sync::Arc; 292 | fn deref(&self) -> &Self::Target { 293 | &self.0 294 | } 295 | } 296 | impl DerefMut for Arc { 297 | fn deref_mut(&mut self) -> &mut Self::Target { 298 | &mut self.0 299 | } 300 | } 301 | impl AsRef> for Arc { 302 | fn as_ref(&self) -> &sync::Arc { 303 | &self.0 304 | } 305 | } 306 | impl AsMut> for Arc { 307 | fn as_mut(&mut self) -> &mut sync::Arc { 308 | &mut self.0 309 | } 310 | } 311 | impl AsRef for Arc { 312 | fn as_ref(&self) -> &T { 313 | &self.0 314 | } 315 | } 316 | impl Borrow for Arc { 317 | fn borrow(&self) -> &T { 318 | &self.0 319 | } 320 | } 321 | impl From> for Arc { 322 | fn from(t: sync::Arc) -> Self { 323 | Self(t) 324 | } 325 | } 326 | impl From> for sync::Arc { 327 | fn from(v: Arc) -> Self { 328 | v.0 329 | } 330 | } 331 | impl From for Arc { 332 | fn from(t: T) -> Self { 333 | Self(sync::Arc::new(t)) 334 | } 335 | } 336 | impl Clone for Arc { 337 | fn clone(&self) -> Self { 338 | Self(self.0.clone()) 339 | } 340 | } 341 | impl fmt::Debug for Arc { 342 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 343 | self.0.fmt(f) 344 | } 345 | } 346 | impl fmt::Display for Arc { 347 | fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { 348 | self.0.fmt(f) 349 | } 350 | } 351 | impl serde::ser::Serialize for Arc { 352 | fn serialize(&self, serializer: S) -> Result 353 | where 354 | S: serde::Serializer, 355 | { 356 | serialize(&self.0, serializer) 357 | } 358 | } 359 | impl<'de, T: Deserialize + ?Sized + 'static> serde::de::Deserialize<'de> for Arc { 360 | fn deserialize(deserializer: D) -> Result 361 | where 362 | D: serde::Deserializer<'de>, 363 | { 364 | deserialize(deserializer).map(Self) 365 | } 366 | } 367 | 368 | /// A convenience trait implemented on all (de)serializable implementors of [`std::any::Any`]. 369 | /// 370 | /// It can be made into a trait object which is then (de)serializable. 371 | /// 372 | /// # Example 373 | /// ``` 374 | /// extern crate serde_json; 375 | /// extern crate serde_traitobject as s; 376 | /// 377 | /// use std::any::Any; 378 | /// 379 | /// let erased: s::Box = s::Box::new(String::from("hi there")); 380 | /// 381 | /// let serialized = serde_json::to_string(&erased).unwrap(); 382 | /// let deserialized: s::Box = serde_json::from_str(&serialized).unwrap(); 383 | /// 384 | /// let downcast: Box = Box::::downcast(deserialized.into_any()).unwrap(); 385 | /// 386 | /// println!("{}!", downcast); 387 | /// # assert_eq!(format!("{}!", downcast), "hi there!"); 388 | /// // hi there! 389 | /// ``` 390 | pub trait Any: any::Any + Serialize + Deserialize { 391 | /// Convert to a `&std::any::Any`. 392 | fn as_any(&self) -> &dyn any::Any; 393 | /// Convert to a `&mut std::any::Any`. 394 | fn as_any_mut(&mut self) -> &mut dyn any::Any; 395 | /// Convert to a `std::boxed::Box`. 396 | fn into_any(self: boxed::Box) -> boxed::Box; 397 | } 398 | impl Any for T 399 | where 400 | T: any::Any + Serialize + Deserialize, 401 | { 402 | fn as_any(&self) -> &dyn any::Any { 403 | self 404 | } 405 | fn as_any_mut(&mut self) -> &mut dyn any::Any { 406 | self 407 | } 408 | fn into_any(self: boxed::Box) -> boxed::Box { 409 | self 410 | } 411 | } 412 | 413 | impl AsRef for dyn Any { 414 | fn as_ref(&self) -> &Self { 415 | self 416 | } 417 | } 418 | impl AsRef for dyn Any + Send { 419 | fn as_ref(&self) -> &Self { 420 | self 421 | } 422 | } 423 | 424 | impl serde::ser::Serialize for dyn Any { 425 | fn serialize(&self, serializer: S) -> Result 426 | where 427 | S: serde::Serializer, 428 | { 429 | serialize(self, serializer) 430 | } 431 | } 432 | impl<'de> serde::de::Deserialize<'de> for boxed::Box { 433 | fn deserialize(deserializer: D) -> Result 434 | where 435 | D: serde::Deserializer<'de>, 436 | { 437 | >::deserialize(deserializer).map(|x| x.0) 438 | } 439 | } 440 | impl serde::ser::Serialize for dyn Any + Send { 441 | fn serialize(&self, serializer: S) -> Result 442 | where 443 | S: serde::Serializer, 444 | { 445 | serialize(self, serializer) 446 | } 447 | } 448 | impl<'de> serde::de::Deserialize<'de> for boxed::Box { 449 | fn deserialize(deserializer: D) -> Result 450 | where 451 | D: serde::Deserializer<'de>, 452 | { 453 | >::deserialize(deserializer).map(|x| x.0) 454 | } 455 | } 456 | 457 | /// A convenience trait implemented on all (de)serializable implementors of [`std::error::Error`]. 458 | /// 459 | /// It can be made into a trait object which is then (de)serializable. 460 | /// 461 | /// # Example 462 | /// ``` 463 | /// # extern crate serde; 464 | /// #[macro_use] extern crate serde_derive; 465 | /// extern crate serde_json; 466 | /// extern crate serde_traitobject as s; 467 | /// 468 | /// use std::fmt; 469 | /// 470 | /// #[derive(Serialize,Deserialize,Debug)] 471 | /// struct MyError(String); 472 | /// impl fmt::Display for MyError { 473 | /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 474 | /// write!(f, "{}", self.0) 475 | /// } 476 | /// } 477 | /// impl std::error::Error for MyError {} 478 | /// 479 | /// fn fallible() -> Result<(),s::Box> { 480 | /// Err(Box::new(MyError(String::from("boxed error"))) as Box)? 481 | /// } 482 | /// 483 | /// let serialized = serde_json::to_string(&fallible()).unwrap(); 484 | /// let deserialized: Result<(),s::Box> = serde_json::from_str(&serialized).unwrap(); 485 | /// 486 | /// println!("{:?}", deserialized); 487 | /// # assert_eq!(format!("{:?}", deserialized), "Err(MyError(\"boxed error\"))"); 488 | /// // Err(MyError("boxed error")) 489 | /// ``` 490 | pub trait Error: error::Error + Serialize + Deserialize {} 491 | impl Error for T where T: error::Error + Serialize + Deserialize {} 492 | 493 | impl<'a> AsRef for dyn Error + 'a { 494 | fn as_ref(&self) -> &Self { 495 | self 496 | } 497 | } 498 | impl<'a> AsRef for dyn Error + Send + 'a { 499 | fn as_ref(&self) -> &Self { 500 | self 501 | } 502 | } 503 | 504 | impl<'a, E: error::Error + Serialize + Deserialize + 'a> From for Box { 505 | fn from(err: E) -> Self { 506 | Box::new(err) 507 | } 508 | } 509 | impl<'a, E: error::Error + Serialize + Deserialize + 'a> From for boxed::Box { 510 | fn from(err: E) -> Self { 511 | boxed::Box::new(err) 512 | } 513 | } 514 | 515 | impl serde::ser::Serialize for dyn Error { 516 | fn serialize(&self, serializer: S) -> Result 517 | where 518 | S: serde::Serializer, 519 | { 520 | serialize(self, serializer) 521 | } 522 | } 523 | impl<'de> serde::de::Deserialize<'de> for boxed::Box { 524 | fn deserialize(deserializer: D) -> Result 525 | where 526 | D: serde::Deserializer<'de>, 527 | { 528 | >::deserialize(deserializer).map(|x| x.0) 529 | } 530 | } 531 | impl serde::ser::Serialize for dyn Error + Send { 532 | fn serialize(&self, serializer: S) -> Result 533 | where 534 | S: serde::Serializer, 535 | { 536 | serialize(self, serializer) 537 | } 538 | } 539 | impl<'de> serde::de::Deserialize<'de> for boxed::Box { 540 | fn deserialize(deserializer: D) -> Result 541 | where 542 | D: serde::Deserializer<'de>, 543 | { 544 | >::deserialize(deserializer).map(|x| x.0) 545 | } 546 | } 547 | 548 | /// A convenience trait implemented on all (de)serializable implementors of [`std::fmt::Display`]. 549 | /// 550 | /// It can be made into a trait object which is then (de)serializable. 551 | /// 552 | /// # Example 553 | /// ``` 554 | /// # extern crate serde; 555 | /// #[macro_use] extern crate serde_derive; 556 | /// extern crate serde_json; 557 | /// extern crate serde_traitobject as s; 558 | /// 559 | /// fn message() -> s::Box { 560 | /// s::Box::new(String::from("boxed displayable")) 561 | /// } 562 | /// 563 | /// let serialized = serde_json::to_string(&message()).unwrap(); 564 | /// let deserialized: s::Box = serde_json::from_str(&serialized).unwrap(); 565 | /// 566 | /// println!("{}", deserialized); 567 | /// # assert_eq!(format!("{}", deserialized), "boxed displayable"); 568 | /// // boxed displayable 569 | /// ``` 570 | pub trait Display: fmt::Display + Serialize + Deserialize {} 571 | impl Display for T where T: fmt::Display + Serialize + Deserialize {} 572 | 573 | impl<'a> AsRef for dyn Display + 'a { 574 | fn as_ref(&self) -> &Self { 575 | self 576 | } 577 | } 578 | impl<'a> AsRef for dyn Display + Send + 'a { 579 | fn as_ref(&self) -> &Self { 580 | self 581 | } 582 | } 583 | 584 | impl serde::ser::Serialize for dyn Display { 585 | fn serialize(&self, serializer: S) -> Result 586 | where 587 | S: serde::Serializer, 588 | { 589 | serialize(self, serializer) 590 | } 591 | } 592 | impl<'de> serde::de::Deserialize<'de> for boxed::Box { 593 | fn deserialize(deserializer: D) -> Result 594 | where 595 | D: serde::Deserializer<'de>, 596 | { 597 | >::deserialize(deserializer).map(|x| x.0) 598 | } 599 | } 600 | impl serde::ser::Serialize for dyn Display + Send { 601 | fn serialize(&self, serializer: S) -> Result 602 | where 603 | S: serde::Serializer, 604 | { 605 | serialize(self, serializer) 606 | } 607 | } 608 | impl<'de> serde::de::Deserialize<'de> for boxed::Box { 609 | fn deserialize(deserializer: D) -> Result 610 | where 611 | D: serde::Deserializer<'de>, 612 | { 613 | >::deserialize(deserializer).map(|x| x.0) 614 | } 615 | } 616 | 617 | /// A convenience trait implemented on all (de)serializable implementors of [`std::fmt::Debug`]. 618 | /// 619 | /// It can be made into a trait object which is then (de)serializable. 620 | /// 621 | /// # Example 622 | /// ``` 623 | /// # extern crate serde; 624 | /// #[macro_use] extern crate serde_derive; 625 | /// extern crate serde_json; 626 | /// extern crate serde_traitobject as s; 627 | /// 628 | /// fn debug() -> s::Box { 629 | /// s::Box::new(String::from("boxed debuggable")) 630 | /// } 631 | /// 632 | /// let serialized = serde_json::to_string(&debug()).unwrap(); 633 | /// let deserialized: s::Box = serde_json::from_str(&serialized).unwrap(); 634 | /// 635 | /// println!("{:?}", deserialized); 636 | /// # assert_eq!(format!("{:?}", deserialized), "\"boxed debuggable\""); 637 | /// // "boxed debuggable" 638 | /// ``` 639 | pub trait Debug: fmt::Debug + Serialize + Deserialize {} 640 | impl Debug for T where T: fmt::Debug + Serialize + Deserialize {} 641 | 642 | impl<'a> AsRef for dyn Debug + 'a { 643 | fn as_ref(&self) -> &Self { 644 | self 645 | } 646 | } 647 | impl<'a> AsRef for dyn Debug + Send + 'a { 648 | fn as_ref(&self) -> &Self { 649 | self 650 | } 651 | } 652 | 653 | impl serde::ser::Serialize for dyn Debug { 654 | fn serialize(&self, serializer: S) -> Result 655 | where 656 | S: serde::Serializer, 657 | { 658 | serialize(self, serializer) 659 | } 660 | } 661 | impl<'de> serde::de::Deserialize<'de> for boxed::Box { 662 | fn deserialize(deserializer: D) -> Result 663 | where 664 | D: serde::Deserializer<'de>, 665 | { 666 | >::deserialize(deserializer).map(|x| x.0) 667 | } 668 | } 669 | impl serde::ser::Serialize for dyn Debug + Send { 670 | fn serialize(&self, serializer: S) -> Result 671 | where 672 | S: serde::Serializer, 673 | { 674 | serialize(self, serializer) 675 | } 676 | } 677 | impl<'de> serde::de::Deserialize<'de> for boxed::Box { 678 | fn deserialize(deserializer: D) -> Result 679 | where 680 | D: serde::Deserializer<'de>, 681 | { 682 | >::deserialize(deserializer).map(|x| x.0) 683 | } 684 | } 685 | 686 | /// A convenience trait implemented on all (de)serializable implementors of [`std::ops::FnOnce`]. 687 | /// 688 | /// It can be made into a trait object which is then (de)serializable. 689 | pub trait FnOnce: ops::FnOnce + Serialize + Deserialize {} 690 | impl FnOnce for T where T: ops::FnOnce + Serialize + Deserialize {} 691 | 692 | impl<'a, Args, Output> AsRef for dyn FnOnce + 'a { 693 | fn as_ref(&self) -> &Self { 694 | self 695 | } 696 | } 697 | impl<'a, Args, Output> AsRef for dyn FnOnce + Send + 'a { 698 | fn as_ref(&self) -> &Self { 699 | self 700 | } 701 | } 702 | 703 | impl serde::ser::Serialize for dyn FnOnce { 704 | fn serialize(&self, serializer: S) -> Result 705 | where 706 | S: serde::Serializer, 707 | { 708 | serialize(self, serializer) 709 | } 710 | } 711 | impl<'de, Args: 'static, Output: 'static> serde::de::Deserialize<'de> 712 | for boxed::Box + 'static> 713 | { 714 | fn deserialize(deserializer: D) -> Result 715 | where 716 | D: serde::Deserializer<'de>, 717 | { 718 | + 'static>>::deserialize(deserializer).map(|x| x.0) 719 | } 720 | } 721 | impl serde::ser::Serialize 722 | for dyn FnOnce + Send 723 | { 724 | fn serialize(&self, serializer: S) -> Result 725 | where 726 | S: serde::Serializer, 727 | { 728 | serialize(self, serializer) 729 | } 730 | } 731 | impl<'de, Args: 'static, Output: 'static> serde::de::Deserialize<'de> 732 | for boxed::Box + Send + 'static> 733 | { 734 | fn deserialize(deserializer: D) -> Result 735 | where 736 | D: serde::Deserializer<'de>, 737 | { 738 | + Send + 'static>>::deserialize(deserializer) 739 | .map(|x| x.0) 740 | } 741 | } 742 | 743 | /// A convenience trait implemented on all (de)serializable implementors of [`std::ops::FnMut`]. 744 | /// 745 | /// It can be made into a trait object which is then (de)serializable. 746 | pub trait FnMut: ops::FnMut + Serialize + Deserialize {} 747 | impl FnMut for T where T: ops::FnMut + Serialize + Deserialize {} 748 | 749 | impl<'a, Args, Output> AsRef for dyn FnMut + 'a { 750 | fn as_ref(&self) -> &Self { 751 | self 752 | } 753 | } 754 | impl<'a, Args, Output> AsRef for dyn FnMut + Send + 'a { 755 | fn as_ref(&self) -> &Self { 756 | self 757 | } 758 | } 759 | 760 | impl serde::ser::Serialize for dyn FnMut { 761 | fn serialize(&self, serializer: S) -> Result 762 | where 763 | S: serde::Serializer, 764 | { 765 | serialize(self, serializer) 766 | } 767 | } 768 | impl<'de, Args: 'static, Output: 'static> serde::de::Deserialize<'de> 769 | for boxed::Box + 'static> 770 | { 771 | fn deserialize(deserializer: D) -> Result 772 | where 773 | D: serde::Deserializer<'de>, 774 | { 775 | + 'static>>::deserialize(deserializer).map(|x| x.0) 776 | } 777 | } 778 | impl serde::ser::Serialize 779 | for dyn FnMut + Send 780 | { 781 | fn serialize(&self, serializer: S) -> Result 782 | where 783 | S: serde::Serializer, 784 | { 785 | serialize(self, serializer) 786 | } 787 | } 788 | impl<'de, Args: 'static, Output: 'static> serde::de::Deserialize<'de> 789 | for boxed::Box + Send + 'static> 790 | { 791 | fn deserialize(deserializer: D) -> Result 792 | where 793 | D: serde::Deserializer<'de>, 794 | { 795 | + Send + 'static>>::deserialize(deserializer) 796 | .map(|x| x.0) 797 | } 798 | } 799 | 800 | /// A convenience trait implemented on all (de)serializable implementors of [`std::ops::Fn`]. 801 | /// 802 | /// It can be made into a trait object which is then (de)serializable. 803 | pub trait Fn: ops::Fn + Serialize + Deserialize {} 804 | impl Fn for T where T: ops::Fn + Serialize + Deserialize {} 805 | 806 | impl<'a, Args, Output> AsRef for dyn Fn + 'a { 807 | fn as_ref(&self) -> &Self { 808 | self 809 | } 810 | } 811 | impl<'a, Args, Output> AsRef for dyn Fn + Send + 'a { 812 | fn as_ref(&self) -> &Self { 813 | self 814 | } 815 | } 816 | 817 | impl serde::ser::Serialize for dyn Fn { 818 | fn serialize(&self, serializer: S) -> Result 819 | where 820 | S: serde::Serializer, 821 | { 822 | serialize(self, serializer) 823 | } 824 | } 825 | impl<'de, Args: 'static, Output: 'static> serde::de::Deserialize<'de> 826 | for boxed::Box + 'static> 827 | { 828 | fn deserialize(deserializer: D) -> Result 829 | where 830 | D: serde::Deserializer<'de>, 831 | { 832 | + 'static>>::deserialize(deserializer).map(|x| x.0) 833 | } 834 | } 835 | impl serde::ser::Serialize 836 | for dyn Fn + Send 837 | { 838 | fn serialize(&self, serializer: S) -> Result 839 | where 840 | S: serde::Serializer, 841 | { 842 | serialize(self, serializer) 843 | } 844 | } 845 | impl<'de, Args: 'static, Output: 'static> serde::de::Deserialize<'de> 846 | for boxed::Box + Send + 'static> 847 | { 848 | fn deserialize(deserializer: D) -> Result 849 | where 850 | D: serde::Deserializer<'de>, 851 | { 852 | + Send + 'static>>::deserialize(deserializer) 853 | .map(|x| x.0) 854 | } 855 | } 856 | 857 | /// Convenience traits implemented on all (de)serializable implementors of [`serde_closure::traits::*`](serde_closure::traits). 858 | #[cfg(feature = "serde_closure")] 859 | pub mod sc { 860 | use super::{serialize, Box, Deserialize, Serialize}; 861 | use serde_closure::traits as sc; 862 | use std::boxed; 863 | 864 | /// A convenience trait implemented on all (de)serializable implementors of [`serde_closure::traits::FnOnce`]. 865 | /// 866 | /// It can be made into a trait object which is then (de)serializable. 867 | pub trait FnOnce: sc::FnOnceBox + Serialize + Deserialize {} 868 | impl FnOnce for T where T: sc::FnOnceBox + Serialize + Deserialize {} 869 | 870 | impl<'a, Args, Output> AsRef for dyn FnOnce + 'a { 871 | fn as_ref(&self) -> &Self { 872 | self 873 | } 874 | } 875 | impl<'a, Args, Output> AsRef for dyn FnOnce + Send + 'a { 876 | fn as_ref(&self) -> &Self { 877 | self 878 | } 879 | } 880 | 881 | impl serde::ser::Serialize for dyn FnOnce { 882 | fn serialize(&self, serializer: S) -> Result 883 | where 884 | S: serde::Serializer, 885 | { 886 | serialize(self, serializer) 887 | } 888 | } 889 | impl<'de, Args: 'static, Output: 'static> serde::de::Deserialize<'de> 890 | for boxed::Box + 'static> 891 | { 892 | fn deserialize(deserializer: D) -> Result 893 | where 894 | D: serde::Deserializer<'de>, 895 | { 896 | + 'static>>::deserialize(deserializer) 897 | .map(|x| x.0) 898 | } 899 | } 900 | impl serde::ser::Serialize 901 | for dyn FnOnce + Send 902 | { 903 | fn serialize(&self, serializer: S) -> Result 904 | where 905 | S: serde::Serializer, 906 | { 907 | serialize(self, serializer) 908 | } 909 | } 910 | impl<'de, Args: 'static, Output: 'static> serde::de::Deserialize<'de> 911 | for boxed::Box + Send + 'static> 912 | { 913 | fn deserialize(deserializer: D) -> Result 914 | where 915 | D: serde::Deserializer<'de>, 916 | { 917 | + Send + 'static>>::deserialize(deserializer) 918 | .map(|x| x.0) 919 | } 920 | } 921 | 922 | /// A convenience trait implemented on all (de)serializable implementors of [`serde_closure::traits::FnMut`]. 923 | /// 924 | /// It can be made into a trait object which is then (de)serializable. 925 | pub trait FnMut: sc::FnMut + Serialize + Deserialize {} 926 | impl FnMut for T where T: sc::FnMut + Serialize + Deserialize {} 927 | 928 | impl<'a, Args, Output> AsRef for dyn FnMut + 'a { 929 | fn as_ref(&self) -> &Self { 930 | self 931 | } 932 | } 933 | impl<'a, Args, Output> AsRef for dyn FnMut + Send + 'a { 934 | fn as_ref(&self) -> &Self { 935 | self 936 | } 937 | } 938 | 939 | impl serde::ser::Serialize for dyn FnMut { 940 | fn serialize(&self, serializer: S) -> Result 941 | where 942 | S: serde::Serializer, 943 | { 944 | serialize(self, serializer) 945 | } 946 | } 947 | impl<'de, Args: 'static, Output: 'static> serde::de::Deserialize<'de> 948 | for boxed::Box + 'static> 949 | { 950 | fn deserialize(deserializer: D) -> Result 951 | where 952 | D: serde::Deserializer<'de>, 953 | { 954 | + 'static>>::deserialize(deserializer) 955 | .map(|x| x.0) 956 | } 957 | } 958 | impl serde::ser::Serialize 959 | for dyn FnMut + Send 960 | { 961 | fn serialize(&self, serializer: S) -> Result 962 | where 963 | S: serde::Serializer, 964 | { 965 | serialize(self, serializer) 966 | } 967 | } 968 | impl<'de, Args: 'static, Output: 'static> serde::de::Deserialize<'de> 969 | for boxed::Box + Send + 'static> 970 | { 971 | fn deserialize(deserializer: D) -> Result 972 | where 973 | D: serde::Deserializer<'de>, 974 | { 975 | + Send + 'static>>::deserialize(deserializer) 976 | .map(|x| x.0) 977 | } 978 | } 979 | 980 | /// A convenience trait implemented on all (de)serializable implementors of [`serde_closure::traits::Fn`]. 981 | /// 982 | /// It can be made into a trait object which is then (de)serializable. 983 | pub trait Fn: sc::Fn + Serialize + Deserialize {} 984 | impl Fn for T where T: sc::Fn + Serialize + Deserialize {} 985 | 986 | impl<'a, Args, Output> AsRef for dyn Fn + 'a { 987 | fn as_ref(&self) -> &Self { 988 | self 989 | } 990 | } 991 | impl<'a, Args, Output> AsRef for dyn Fn + Send + 'a { 992 | fn as_ref(&self) -> &Self { 993 | self 994 | } 995 | } 996 | 997 | impl serde::ser::Serialize for dyn Fn { 998 | fn serialize(&self, serializer: S) -> Result 999 | where 1000 | S: serde::Serializer, 1001 | { 1002 | serialize(self, serializer) 1003 | } 1004 | } 1005 | impl<'de, Args: 'static, Output: 'static> serde::de::Deserialize<'de> 1006 | for boxed::Box + 'static> 1007 | { 1008 | fn deserialize(deserializer: D) -> Result 1009 | where 1010 | D: serde::Deserializer<'de>, 1011 | { 1012 | + 'static>>::deserialize(deserializer).map(|x| x.0) 1013 | } 1014 | } 1015 | impl serde::ser::Serialize 1016 | for dyn Fn + Send 1017 | { 1018 | fn serialize(&self, serializer: S) -> Result 1019 | where 1020 | S: serde::Serializer, 1021 | { 1022 | serialize(self, serializer) 1023 | } 1024 | } 1025 | impl<'de, Args: 'static, Output: 'static> serde::de::Deserialize<'de> 1026 | for boxed::Box + Send + 'static> 1027 | { 1028 | fn deserialize(deserializer: D) -> Result 1029 | where 1030 | D: serde::Deserializer<'de>, 1031 | { 1032 | + Send + 'static>>::deserialize(deserializer) 1033 | .map(|x| x.0) 1034 | } 1035 | } 1036 | } 1037 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! Serializable and deserializable trait objects. 2 | //! 3 | //! **[Crates.io](https://crates.io/crates/serde_traitobject) │ [Repo](https://github.com/alecmocatta/serde_traitobject)** 4 | //! 5 | //! This library enables the serialization and deserialization of trait objects so they can be sent between other processes running the same binary. 6 | //! 7 | //! 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. 8 | //! 9 | //! Any trait can be made (de)serializable when made into a trait object by adding this crate's [Serialize] and [Deserialize] traits as supertraits: 10 | //! 11 | //! ``` 12 | //! # use serde_derive::{Serialize, Deserialize}; 13 | //! trait MyTrait: serde_traitobject::Serialize + serde_traitobject::Deserialize { 14 | //! fn my_method(&self); 15 | //! } 16 | //! 17 | //! #[derive(Serialize, Deserialize)] 18 | //! struct Message { 19 | //! #[serde(with = "serde_traitobject")] 20 | //! message: Box, 21 | //! } 22 | //! 23 | //! // Woohoo, `Message` is now serializable! 24 | //! ``` 25 | //! 26 | //! 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. 27 | //! 28 | //! There are two ways to (de)serialize your trait object: 29 | //! * Apply the `#[serde(with = "serde_traitobject")]` [field attribute](https://serde.rs/attributes.html), which instructs serde to use this crate's [serialize](serialize()) and [deserialize](deserialize()) functions; 30 | //! * The [Box], [Rc] and [Arc] structs, which are simple wrappers around their stdlib counterparts that automatically handle (de)serialization without needing the above annotation; 31 | //! 32 | //! Additionally, there are several convenience traits implemented that extend their stdlib counterparts: 33 | //! 34 | //! * [Any], [Debug], [Display], [Error], [Fn], [FnMut], [FnOnce] 35 | //! 36 | //! These are automatically implemented on all implementors of their stdlib counterparts that also implement `serde::Serialize` and `serde::de::DeserializeOwned`. 37 | //! 38 | //! ``` 39 | //! # use serde_derive::{Serialize, Deserialize}; 40 | //! use std::any::Any; 41 | //! use serde_traitobject as s; 42 | //! 43 | //! #[derive(Serialize, Deserialize, Debug)] 44 | //! struct MyStruct { 45 | //! foo: String, 46 | //! bar: usize, 47 | //! } 48 | //! 49 | //! let my_struct = MyStruct { 50 | //! foo: String::from("abc"), 51 | //! bar: 123, 52 | //! }; 53 | //! 54 | //! let erased: s::Box = s::Box::new(my_struct); 55 | //! 56 | //! let serialized = serde_json::to_string(&erased).unwrap(); 57 | //! let deserialized: s::Box = serde_json::from_str(&serialized).unwrap(); 58 | //! 59 | //! let downcast: Box = Box::::downcast(deserialized.into_any()).unwrap(); 60 | //! 61 | //! println!("{:?}", downcast); 62 | //! # assert_eq!(format!("{:?}", downcast), "MyStruct { foo: \"abc\", bar: 123 }"); 63 | //! // MyStruct { foo: "abc", bar: 123 } 64 | //! ``` 65 | //! 66 | //! # Security 67 | //! 68 | //! This crate works by wrapping the vtable pointer with [`relative::Vtable`](https://docs.rs/relative) such that it can safely be sent between processes. 69 | //! 70 | //! 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. 71 | //! 72 | //! See [here](https://github.com/rust-lang/rust/pull/66113) for ongoing work in `rustc` to make this safe and secure. 73 | //! 74 | //! ## Validation 75 | //! 76 | //! Three things are serialized alongside the vtable pointer for the purpose of validation: 77 | //! 78 | //! * the [`build_id`](https://github.com/alecmocatta/build_id) (128 bits); 79 | //! * the [`type_id`](https://doc.rust-lang.org/std/intrinsics/fn.type_id.html) of the trait object (64 bits); 80 | //! * the `type_id` of the concrete type (64 bits). 81 | //! 82 | //! At some point in Rust's future, I think it would be great if the latter could be used to safely look up and create a trait object. As it is, that functionality doesn't exist yet, so what this crate does instead is serialize the vtable pointer (relative to a static base), and do as much validity checking as it reasonably can before it can be used and potentially invoke UB. 83 | //! 84 | //! The first two are [checked for validity](https://github.com/alecmocatta/relative/blob/dae206663a09b9c0c4b3012c528b0e9c063df742/src/lib.rs#L457-L474) before usage of the vtable pointer. The `build_id` ensures that the vtable pointer came from an invocation of an identically laid out binary1. The `type_id` ensures that the trait object being deserialized is the same type as the trait object that was serialized. They ensure that under non-malicious conditions, attempts to deserialize invalid data return an error rather than UB. The `type_id` of the concrete type is used as a [sanity check](https://github.com/alecmocatta/serde_traitobject/blob/b20d74e183063e7d49aff2eabc9dcd5bc26d7c07/src/lib.rs#L469) that panics if it differs from the `type_id` of the concrete type to be deserialized. 85 | //! 86 | //! Regarding collisions, the 128 bit `build_id` colliding is sufficiently unlikely that it can be relied upon to never occur. The 64 bit `type_id` colliding is possible, see [rust-lang/rust#10389](https://github.com/rust-lang/rust/issues/10389), though exceedingly unlikely to occur in practise. 87 | //! 88 | //! The vtable pointer is (de)serialized as a usize relative to the vtable pointer of [this static trait object](https://github.com/alecmocatta/relative/blob/dae206663a09b9c0c4b3012c528b0e9c063df742/src/lib.rs#L90). This enables it to work under typical dynamic linking conditions, where the absolute vtable addresses can differ across invocations of the same binary, but relative addresses remain constant. 89 | //! 90 | //! All together this leaves, as far as I'm aware, three soundness holes: 91 | //! 92 | //! * A malicious user with a copy of the binary could trivially craft a `build_id` and `type_id` that pass validation and gives them control of where to jump to. 93 | //! * Data corruption of the serialized vtable pointer but not the `build_id` or `type_id` used for validation, resulting in a jump to an arbitrary address. This could be rectified in a future version of this library by using a cipher to make it vanishingly unlikely for corruptions to affect only the vtable pointer, by mixing the vtable pointer and validation components upon (de)serialization. 94 | //! * Dynamic linking conditions where the relative addresses (vtable - static vtable) are different across different invocations of the same binary. I'm sure this is possible, but it's not a scenario I've encountered so I can't speak to its commonness. 95 | //! 96 | //! 1I don't think this requirement is strictly necessary, as the `type_id` should include all information that could affect soundness (trait methods, calling conventions, etc), but it's included in case that doesn't hold in practise; to provide a more helpful error message; and to reduce the likelihood of collisions. 97 | //! 98 | //! # Note 99 | //! 100 | //! This crate currently requires Rust nightly. 101 | 102 | #![doc(html_root_url = "https://docs.rs/serde_traitobject/0.2.9")] 103 | #![feature( 104 | arbitrary_self_types_pointers, 105 | coerce_unsized, 106 | fn_traits, 107 | tuple_trait, 108 | specialization, 109 | unboxed_closures, 110 | unsize 111 | )] 112 | #![warn( 113 | missing_copy_implementations, 114 | missing_debug_implementations, 115 | missing_docs, 116 | trivial_casts, 117 | trivial_numeric_casts, 118 | unused_import_braces, 119 | unused_qualifications, 120 | unused_results, 121 | clippy::pedantic 122 | )] // from https://github.com/rust-unofficial/patterns/blob/master/anti_patterns/deny-warnings.md 123 | #![allow( 124 | clippy::must_use_candidate, 125 | clippy::missing_errors_doc, 126 | incomplete_features 127 | )] 128 | 129 | mod convenience; 130 | 131 | use metatype::type_coerce; 132 | use relative::Vtable; 133 | use serde::ser::SerializeTuple; 134 | use std::{any::type_name, boxed, fmt, marker}; 135 | 136 | pub use convenience::*; 137 | 138 | /// Any trait with this as a supertrait can be serialized as a trait object. 139 | /// 140 | /// It is automatically implemented for all `T: serde::Serialize`, i.e. you should not implement it manually. 141 | /// 142 | /// To use, simply add it as a supertrait to your trait: 143 | /// ``` 144 | /// use serde_derive::{Serialize, Deserialize}; 145 | /// 146 | /// trait MyTrait: serde_traitobject::Serialize + serde_traitobject::Deserialize { 147 | /// fn my_method(&self); 148 | /// } 149 | /// ``` 150 | /// 151 | /// Now your trait object is serializable! 152 | /// ``` 153 | /// # use serde_derive::{Serialize, Deserialize}; 154 | /// # 155 | /// # trait MyTrait: serde_traitobject::Serialize + serde_traitobject::Deserialize { 156 | /// # fn my_method(&self); 157 | /// # } 158 | /// #[derive(Serialize, Deserialize)] 159 | /// struct Message(#[serde(with = "serde_traitobject")] Box); 160 | /// 161 | /// // Woohoo, `Message` is now serializable! 162 | /// ``` 163 | /// 164 | /// Any implementers of `MyTrait` would now have to themselves implement `serde::Serialize` and `serde::de::DeserializeOwned`. This would typically be through `serde_derive`, like: 165 | /// ``` 166 | /// # use serde_derive::{Serialize, Deserialize}; 167 | /// # trait MyTrait: serde_traitobject::Serialize + serde_traitobject::Deserialize { 168 | /// # fn my_method(&self); 169 | /// # } 170 | /// # #[derive(Serialize, Deserialize)] 171 | /// # struct Message(#[serde(with = "serde_traitobject")] Box); 172 | /// #[derive(Serialize, Deserialize)] 173 | /// struct MyStruct { 174 | /// foo: String, 175 | /// } 176 | /// 177 | /// impl MyTrait for MyStruct { 178 | /// fn my_method(&self) { 179 | /// println!("foo: {}", self.foo); 180 | /// } 181 | /// } 182 | /// ``` 183 | pub trait Serialize: serialize::Sealed {} 184 | impl Serialize for T {} 185 | 186 | /// Any trait with this as a supertrait can be deserialized as a boxed trait object. 187 | /// 188 | /// It is automatically implemented for all `T: serde::de::DeserializeOwned`, i.e. you should not implement it manually. 189 | /// 190 | /// To use, simply add it as a supertrait to your trait: 191 | /// ``` 192 | /// use serde_derive::{Serialize, Deserialize}; 193 | /// 194 | /// trait MyTrait: serde_traitobject::Serialize + serde_traitobject::Deserialize { 195 | /// fn my_method(&self); 196 | /// } 197 | /// ``` 198 | /// 199 | /// Now your trait object is serializable! 200 | /// ``` 201 | /// # use serde_derive::{Serialize, Deserialize}; 202 | /// # 203 | /// # trait MyTrait: serde_traitobject::Serialize + serde_traitobject::Deserialize { 204 | /// # fn my_method(&self); 205 | /// # } 206 | /// #[derive(Serialize, Deserialize)] 207 | /// struct Message(#[serde(with = "serde_traitobject")] Box); 208 | /// 209 | /// // Woohoo, `Message` is now serializable! 210 | /// ``` 211 | /// 212 | /// Any implementers of `MyTrait` would now have to themselves implement `serde::Serialize` and `serde::de::DeserializeOwned`. This would typically be through `serde_derive`, like: 213 | /// ``` 214 | /// # use serde_derive::{Serialize, Deserialize}; 215 | /// # trait MyTrait: serde_traitobject::Serialize + serde_traitobject::Deserialize { 216 | /// # fn my_method(&self); 217 | /// # } 218 | /// # #[derive(Serialize, Deserialize)] 219 | /// # struct Message(#[serde(with = "serde_traitobject")] Box); 220 | /// #[derive(Serialize, Deserialize)] 221 | /// struct MyStruct { 222 | /// foo: String, 223 | /// } 224 | /// 225 | /// impl MyTrait for MyStruct { 226 | /// fn my_method(&self) { 227 | /// println!("foo: {}", self.foo); 228 | /// } 229 | /// } 230 | /// ``` 231 | pub trait Deserialize: deserialize::Sealed {} 232 | impl Deserialize for T {} 233 | impl Deserialize for str {} 234 | impl Deserialize for [T] {} 235 | 236 | mod serialize { 237 | use metatype::type_id; 238 | 239 | pub trait Sealed: erased_serde::Serialize { 240 | fn serialize_sized(&self, serializer: S) -> Result 241 | where 242 | S: serde::Serializer, 243 | Self: Sized; 244 | 245 | #[inline] 246 | fn type_id(&self) -> u64 247 | where 248 | Self: 'static, 249 | { 250 | type_id::() 251 | } 252 | } 253 | 254 | impl Sealed for T { 255 | #[inline] 256 | default fn serialize_sized(&self, serializer: S) -> Result 257 | where 258 | S: serde::Serializer, 259 | Self: Sized, 260 | { 261 | let _ = serializer; 262 | unreachable!() 263 | } 264 | } 265 | 266 | impl Sealed for T { 267 | #[inline] 268 | fn serialize_sized(&self, serializer: S) -> Result 269 | where 270 | S: serde::Serializer, 271 | Self: Sized, 272 | { 273 | serde::ser::Serialize::serialize(self, serializer) 274 | } 275 | } 276 | } 277 | 278 | mod deserialize { 279 | use metatype::type_id; 280 | use std::ptr::NonNull; 281 | 282 | pub trait Sealed { 283 | fn deserialize_erased( 284 | self: *const Self, deserializer: &mut dyn erased_serde::Deserializer, 285 | ) -> Result, erased_serde::Error> { 286 | let _ = deserializer; 287 | unreachable!() 288 | } 289 | 290 | fn deserialize_box<'de, D>(deserializer: D) -> Result, D::Error> 291 | where 292 | D: serde::Deserializer<'de>, 293 | Self: Sized, 294 | { 295 | let _ = deserializer; 296 | unreachable!() 297 | } 298 | 299 | #[inline] 300 | fn type_id(self: *const Self) -> u64 301 | where 302 | Self: 'static, 303 | { 304 | type_id::() 305 | } 306 | } 307 | 308 | impl Sealed for T { 309 | #[inline] 310 | fn deserialize_erased( 311 | self: *const Self, deserializer: &mut dyn erased_serde::Deserializer, 312 | ) -> Result, erased_serde::Error> { 313 | erased_serde::deserialize::(deserializer) 314 | .map(|x| NonNull::new(Box::into_raw(Box::new(x)).cast()).unwrap()) 315 | } 316 | 317 | #[inline] 318 | fn deserialize_box<'de, D>(deserializer: D) -> Result, D::Error> 319 | where 320 | D: serde::Deserializer<'de>, 321 | Self: Sized, 322 | { 323 | serde::de::Deserialize::deserialize(deserializer).map(Box::new) 324 | } 325 | } 326 | 327 | impl Sealed for str {} 328 | impl Sealed for [T] {} 329 | 330 | /// Rust currently doesn't support returning Self traitobjects from 331 | /// traitobject methods. Work around that by returning a thin pointer and 332 | /// fattening it. 333 | #[allow(clippy::module_name_repetitions)] 334 | #[inline] 335 | pub fn deserialize_erased( 336 | self_: *const T, deserializer: &mut dyn erased_serde::Deserializer, 337 | ) -> Result, erased_serde::Error> 338 | where 339 | T: Sealed, 340 | { 341 | self_.deserialize_erased(deserializer).map(|raw| { 342 | let object: *mut T = metatype::Type::fatten(raw.as_ptr(), metatype::Type::meta(self_)); 343 | unsafe { Box::from_raw(object) } 344 | }) 345 | } 346 | } 347 | 348 | /// Using a struct + trait to leverage specialisation to respectively handle 349 | /// concrete, slices and traitobjects. 350 | struct Serializer(marker::PhantomData); 351 | trait SerializerTrait { 352 | fn serialize(t: &T, serializer: S) -> Result 353 | where 354 | S: serde::Serializer; 355 | } 356 | impl SerializerTrait for Serializer { 357 | #[inline] 358 | fn serialize(t: &T, serializer: S) -> Result 359 | where 360 | S: serde::Serializer, 361 | { 362 | t.serialize_sized(serializer) 363 | } 364 | } 365 | impl SerializerTrait for Serializer { 366 | #[inline] 367 | fn serialize(t: &str, serializer: S) -> Result 368 | where 369 | S: serde::Serializer, 370 | { 371 | serde::ser::Serialize::serialize(t, serializer) 372 | } 373 | } 374 | impl SerializerTrait<[T]> for Serializer<[T]> { 375 | #[inline] 376 | fn serialize(t: &[T], serializer: S) -> Result 377 | where 378 | S: serde::Serializer, 379 | { 380 | serde::ser::Serialize::serialize(t, serializer) 381 | } 382 | } 383 | impl SerializerTrait for Serializer { 384 | #[inline] 385 | default fn serialize(t: &T, serializer: S) -> Result 386 | where 387 | S: serde::Serializer, 388 | { 389 | let vtable = if let metatype::MetaType::TraitObject = metatype::Type::meta_type(t) { 390 | let trait_object: metatype::TraitObject = type_coerce(metatype::Type::meta(t)); 391 | trait_object.vtable 392 | } else { 393 | panic!() 394 | }; 395 | let mut tup = serializer.serialize_tuple(3)?; 396 | // We're making the assumption that the vtable is positioned the same 397 | // relative to the base vtable in every invocation, through e.g. being 398 | // in the same segment, or the binary being statically linked. 399 | // 400 | // See the [`relative`](https://github.com/alecmocatta/relative) crate 401 | // for more information. 402 | tup.serialize_element::>(&unsafe { Vtable::::from(vtable) })?; 403 | tup.serialize_element::(&::type_id(t))?; 404 | tup.serialize_element::>(&SerializeErased(t))?; 405 | tup.end() 406 | } 407 | } 408 | struct SerializeErased<'a, T: Serialize + ?Sized + 'a>(&'a T); 409 | impl<'a, T: Serialize + ?Sized> serde::ser::Serialize for SerializeErased<'a, T> { 410 | #[inline] 411 | fn serialize(&self, serializer: S) -> Result 412 | where 413 | S: serde::Serializer, 414 | { 415 | erased_serde::serialize(self.0, serializer) 416 | } 417 | } 418 | 419 | /// Using a struct + trait to leverage specialisation to respectively handle 420 | /// concrete, slices and traitobjects. 421 | struct Deserializer(marker::PhantomData); 422 | trait DeserializerTrait { 423 | fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> 424 | where 425 | D: serde::Deserializer<'de>; 426 | } 427 | impl DeserializerTrait for Deserializer { 428 | #[inline] 429 | fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> 430 | where 431 | D: serde::Deserializer<'de>, 432 | { 433 | ::deserialize_box(deserializer) 434 | } 435 | } 436 | impl DeserializerTrait for Deserializer { 437 | #[inline] 438 | fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> 439 | where 440 | D: serde::Deserializer<'de>, 441 | { 442 | serde::de::Deserialize::deserialize(deserializer) 443 | } 444 | } 445 | impl DeserializerTrait<[T]> for Deserializer<[T]> { 446 | #[inline] 447 | fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> 448 | where 449 | D: serde::Deserializer<'de>, 450 | { 451 | serde::de::Deserialize::deserialize(deserializer) 452 | } 453 | } 454 | impl DeserializerTrait for Deserializer { 455 | #[inline] 456 | default fn deserialize<'de, D>(deserializer: D) -> Result, D::Error> 457 | where 458 | D: serde::Deserializer<'de>, 459 | { 460 | struct Visitor(marker::PhantomData); 461 | impl<'de, T: Deserialize + ?Sized + 'static> serde::de::Visitor<'de> for Visitor { 462 | type Value = boxed::Box; 463 | fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 464 | write!(formatter, "a \"{}\" trait object", type_name::()) 465 | } 466 | #[inline] 467 | fn visit_seq(self, mut seq: A) -> Result, A::Error> 468 | where 469 | A: serde::de::SeqAccess<'de>, 470 | { 471 | let t0: Vtable = match seq.next_element()? { 472 | Some(value) => value, 473 | None => return Err(serde::de::Error::invalid_length(0, &self)), 474 | }; 475 | let t1: u64 = match seq.next_element()? { 476 | Some(value) => value, 477 | None => return Err(serde::de::Error::invalid_length(1, &self)), 478 | }; 479 | let meta = metatype::TraitObject { vtable: t0.to() }; 480 | let object: *const T = metatype::Type::dangling(type_coerce(meta)).as_ptr(); 481 | assert_eq!(t1, object.type_id(), "Deserializing the trait object \"{}\" failed in a way that should never happen. Please file an issue! https://github.com/alecmocatta/serde_traitobject/issues/new", type_name::()); 482 | let t2: boxed::Box = match seq.next_element_seed(DeserializeErased(object))? { 483 | Some(value) => value, 484 | None => return Err(serde::de::Error::invalid_length(2, &self)), 485 | }; 486 | Ok(t2) 487 | } 488 | } 489 | deserializer.deserialize_tuple(3, Visitor(marker::PhantomData)) 490 | } 491 | } 492 | struct DeserializeErased(*const T); 493 | impl<'de, T: Deserialize + ?Sized> serde::de::DeserializeSeed<'de> for DeserializeErased { 494 | type Value = boxed::Box; 495 | 496 | #[inline] 497 | fn deserialize(self, deserializer: D) -> Result 498 | where 499 | D: serde::de::Deserializer<'de>, 500 | { 501 | let deserializer = &mut ::erase(deserializer); 502 | deserialize::deserialize_erased(self.0, deserializer).map_err(serde::de::Error::custom) 503 | } 504 | } 505 | 506 | /// Serialize a value by reference. 507 | /// 508 | /// This is intended to enable: 509 | /// ``` 510 | /// # use serde_derive::{Serialize, Deserialize}; 511 | /// 512 | /// #[derive(Serialize, Deserialize)] 513 | /// struct MyStruct { 514 | /// #[serde(with = "serde_traitobject")] 515 | /// field: Box, 516 | /// } 517 | /// ``` 518 | /// 519 | /// Or, alternatively, if only Serialize is desired: 520 | /// ``` 521 | /// # use serde_derive::{Serialize, Deserialize}; 522 | /// 523 | /// #[derive(Serialize)] 524 | /// struct MyStruct { 525 | /// #[serde(serialize_with = "serde_traitobject::serialize")] 526 | /// field: Box, 527 | /// } 528 | /// ``` 529 | pub fn serialize + ?Sized, S>( 530 | t: &B, serializer: S, 531 | ) -> Result 532 | where 533 | S: serde::Serializer, 534 | { 535 | Serializer::::serialize(t.as_ref(), serializer) 536 | } 537 | 538 | /// Deserialize a value `T` into `B` where `Box: Into`. 539 | /// 540 | /// This is intended to enable: 541 | /// ``` 542 | /// # use serde_derive::{Serialize, Deserialize}; 543 | /// 544 | /// #[derive(Serialize, Deserialize)] 545 | /// struct MyStruct { 546 | /// #[serde(with = "serde_traitobject")] 547 | /// field: Box, 548 | /// } 549 | /// ``` 550 | /// 551 | /// Or, alternatively, if only Deserialize is desired: 552 | /// ``` 553 | /// # use serde_derive::{Serialize, Deserialize}; 554 | /// 555 | /// #[derive(Deserialize)] 556 | /// struct MyStruct { 557 | /// #[serde(deserialize_with = "serde_traitobject::deserialize")] 558 | /// field: Box, 559 | /// } 560 | /// ``` 561 | pub fn deserialize<'de, T: Deserialize + ?Sized + 'static, B, D>( 562 | deserializer: D, 563 | ) -> Result 564 | where 565 | D: serde::Deserializer<'de>, 566 | boxed::Box: Into, 567 | { 568 | Deserializer::::deserialize(deserializer).map( as Into>::into) 569 | } 570 | -------------------------------------------------------------------------------- /tests/test.rs: -------------------------------------------------------------------------------- 1 | #![feature(unboxed_closures)] 2 | #![warn( 3 | missing_copy_implementations, 4 | missing_debug_implementations, 5 | // missing_docs, 6 | trivial_numeric_casts, 7 | unused_extern_crates, 8 | unused_import_braces, 9 | unused_qualifications, 10 | unused_results, 11 | clippy::pedantic 12 | )] // from https://github.com/rust-unofficial/patterns/blob/master/anti_patterns/deny-warnings.md 13 | #![allow(clippy::unseparated_literal_suffix, dead_code)] 14 | 15 | use serde_closure::Fn; 16 | use serde_derive::{Deserialize, Serialize}; 17 | use serde_traitobject as st; 18 | use serde_traitobject::{Deserialize, Serialize}; 19 | use std::{any, env, process, rc}; 20 | use wasm_bindgen_test::wasm_bindgen_test; 21 | 22 | #[allow(clippy::box_collection)] 23 | #[derive(Serialize, Deserialize)] 24 | struct Abc { 25 | #[serde(with = "st")] 26 | a: rc::Rc, 27 | b: st::Rc, 28 | #[serde(with = "st")] 29 | c: Box, 30 | d: st::Box, 31 | #[serde(with = "st")] 32 | e: Box, 33 | f: st::Box, 34 | g: st::Box String>, 35 | h: st::Box, 36 | i: st::Box, 37 | j: st::Box, 38 | #[serde(with = "st")] 39 | k: Box, 40 | l: st::Box, 41 | #[serde(with = "st")] 42 | m: Box, 43 | n: st::Box<[u16]>, 44 | #[serde(with = "st")] 45 | o: Box<[u16]>, 46 | } 47 | 48 | #[derive(Serialize)] 49 | struct Def<'a> { 50 | a: &'a (dyn st::FnOnce<(), Output = ()> + 'static), 51 | c: &'a mut (dyn st::FnOnce<(), Output = ()> + 'static), 52 | } 53 | 54 | trait Hello { 55 | fn hi(&self) -> String; 56 | } 57 | trait HelloSerialize: Hello + Serialize + Deserialize {} 58 | impl HelloSerialize for T where T: Hello + Serialize + Deserialize {} 59 | 60 | impl Hello for u32 { 61 | fn hi(&self) -> String { 62 | format!("hi u32! {self:?}") 63 | } 64 | } 65 | impl Hello for u16 { 66 | fn hi(&self) -> String { 67 | format!("hi u16! {self:?}") 68 | } 69 | } 70 | impl Hello for u8 { 71 | fn hi(&self) -> String { 72 | format!("hi u8! {self:?}") 73 | } 74 | } 75 | 76 | #[derive(Serialize)] 77 | struct Ghi<'a> { 78 | #[serde(with = "st")] 79 | e: &'a (dyn Hello2Serialize + 'static), 80 | } 81 | trait Hello2 {} 82 | trait Hello2Serialize: Hello2 + Serialize + Deserialize {} 83 | impl Hello2Serialize for T where T: Hello2 + Serialize + Deserialize {} 84 | impl<'a> AsRef for dyn Hello2Serialize { 85 | fn as_ref(&self) -> &(dyn Hello2Serialize + 'a) { 86 | self 87 | } 88 | } 89 | 90 | type Request = st::Box st::FnOnce<(&'a String,), Output = ()> + Send>; 91 | 92 | fn _assert() { 93 | fn assert_serializable() 94 | where 95 | T: serde::Serialize + for<'de> serde::Deserialize<'de>, 96 | { 97 | } 98 | assert_serializable::(); 99 | } 100 | 101 | #[wasm_bindgen_test] 102 | #[allow(clippy::too_many_lines)] 103 | fn main() { 104 | let test = |Abc { 105 | a, 106 | b, 107 | c, 108 | d, 109 | e, 110 | f, 111 | g, 112 | h, 113 | i, 114 | j, 115 | k, 116 | l, 117 | m, 118 | n, 119 | o, 120 | }| { 121 | assert_eq!(a.hi(), "hi u16! 123"); 122 | assert_eq!(b.hi(), "hi u16! 456"); 123 | assert_eq!(c.hi(), "hi u32! 789"); 124 | assert_eq!(d.hi(), "hi u8! 101"); 125 | assert_eq!( 126 | *Box::::downcast::(e.into_any()).unwrap(), 127 | 78 128 | ); 129 | assert_eq!( 130 | *Box::::downcast::(f.into_any()).unwrap(), 131 | 78 132 | ); 133 | assert_eq!(g(22), "hey 123!"); 134 | assert_eq!( 135 | ***Box::::downcast::>(h.into_any()).unwrap(), 136 | 987_654_321 137 | ); 138 | assert_eq!( 139 | *Box::::downcast::( 140 | Box::::downcast::>(i.into_any()) 141 | .unwrap() 142 | .into_any() 143 | ) 144 | .unwrap(), 145 | 987_654_321 146 | ); 147 | assert_eq!(**j, "abc"); 148 | assert_eq!(*k, "def"); 149 | assert_eq!(&**l, "ghi"); 150 | assert_eq!(&*m, "jkl"); 151 | assert_eq!(&**n, &[1u16, 2, 3]); 152 | assert_eq!(&*o, &[1u16, 2, 3]); 153 | }; 154 | 155 | for _ in 0..1_000 { 156 | let a: Box = Box::new(Box::new(1usize) as Box); 157 | let a: Box> = Box::::downcast(a).unwrap(); 158 | let a: Box = *a; 159 | let _: Box = Box::::downcast(a).unwrap(); 160 | 161 | let a: st::Box = st::Box::new(st::Box::new(1usize) as st::Box); 162 | let a: Box = a.into_any(); 163 | let a: Box> = Box::::downcast(a).unwrap(); 164 | let a: st::Box = *a; 165 | let a: Box = a.into_any(); 166 | let _: Box = Box::::downcast(a).unwrap(); 167 | 168 | let original = Abc { 169 | a: rc::Rc::new(123u16), 170 | b: st::Rc::new(456u16), 171 | c: Box::new(789u32), 172 | d: st::Box::new(101u8), 173 | e: Box::new(78u8), 174 | f: st::Box::new(78u8), 175 | g: st::Box::new(Fn!(|a: usize| format!("hey {}!", a + 101))), 176 | h: st::Box::new(st::Box::new(987_654_321usize)), 177 | i: st::Box::new(st::Box::new(987_654_321usize) as st::Box), 178 | j: st::Box::new(String::from("abc")), 179 | k: Box::new(String::from("def")), 180 | l: Into::>::into(String::from("ghi")).into(), 181 | m: String::from("jkl").into(), 182 | n: Into::>::into(vec![1u16, 2, 3]).into(), 183 | o: vec![1u16, 2, 3].into(), 184 | }; 185 | let a1 = serde_json::to_string(&original).unwrap(); 186 | let a2 = bincode::serialize(&original).unwrap(); 187 | let a1 = serde_json::from_str(&a1).unwrap(); 188 | let a2 = bincode::deserialize(&a2).unwrap(); 189 | test(a1); 190 | test(a2); 191 | let a1 = serde_json::to_string(&(st::Box::new(78u8) as st::Box)).unwrap(); 192 | let a1r: Result, _> = serde_json::from_str(&a1); 193 | assert!(a1r.is_ok()); 194 | let a1r: Result, _> = serde_json::from_str(&a1); 195 | assert!(a1r.is_err()); 196 | let a1 = bincode::serialize(&(st::Box::new(78u8) as st::Box)).unwrap(); 197 | let a1: Result, _> = bincode::deserialize(&a1); 198 | assert!(a1.is_err()); 199 | } 200 | 201 | let original = Abc { 202 | a: rc::Rc::new(123u16), 203 | b: st::Rc::new(456u16), 204 | c: Box::new(789u32), 205 | d: st::Box::new(101u8), 206 | e: Box::new(78u8), 207 | f: st::Box::new(78u8), 208 | g: st::Box::new(Fn!(|a: usize| format!("hey {}!", a + 101))), 209 | h: st::Box::new(st::Box::new(987_654_321usize)), 210 | i: st::Box::new(st::Box::new(987_654_321usize) as st::Box), 211 | j: st::Box::new(String::from("abc")), 212 | k: Box::new(String::from("def")), 213 | l: Into::>::into(String::from("ghi")).into(), 214 | m: String::from("jkl").into(), 215 | n: Into::>::into(vec![1u16, 2, 3]).into(), 216 | o: vec![1u16, 2, 3].into(), 217 | }; 218 | 219 | if cfg!(target_arch = "wasm32") { 220 | return; 221 | } 222 | 223 | if let Ok(x) = env::var("SERDE_TRAITOBJECT_SPAWNED") { 224 | let (a, bc): (_, Vec) = serde_json::from_str(&x).unwrap(); 225 | eq(&original, &a); 226 | let b = bincode::deserialize(&bc).unwrap(); 227 | eq(&original, &b); 228 | test(a); 229 | test(b); 230 | process::exit(0); 231 | } 232 | 233 | let exe = env::current_exe().unwrap(); 234 | for i in 0..100 { 235 | println!("{i}"); 236 | let output = process::Command::new(&exe) 237 | .stdin(process::Stdio::null()) 238 | .stdout(process::Stdio::inherit()) 239 | .stderr(process::Stdio::inherit()) 240 | .env( 241 | "SERDE_TRAITOBJECT_SPAWNED", 242 | serde_json::to_string(&(&original, bincode::serialize(&original).unwrap())) 243 | .unwrap(), 244 | ) 245 | .output() 246 | .unwrap(); 247 | assert!(output.status.success(), "{i}: {output:?}", i = i); 248 | } 249 | } 250 | 251 | fn eq(_: &T, _: &T) {} 252 | --------------------------------------------------------------------------------