├── .gitignore ├── Cargo.toml ├── LICENSE-APACHE ├── LICENSE-MIT ├── README.md ├── assets ├── almost-foo.png ├── async-stack.png └── moves-invalidate-pointer.png ├── escher-derive ├── Cargo.toml ├── README.md └── src │ └── lib.rs └── escher ├── Cargo.toml ├── README.md ├── README.tpl └── src ├── escher.rs ├── lib.rs └── tests.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | 3 | members = [ 4 | "escher", 5 | "escher-derive" 6 | ] 7 | -------------------------------------------------------------------------------- /LICENSE-APACHE: -------------------------------------------------------------------------------- 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: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | serde/LICENSE-MIT at master · serde-rs/serde · GitHub 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 |
146 | Skip to content 147 | 148 | 149 | 150 | 151 | 152 | 153 | 503 | 504 |
505 | 506 |
507 | 508 | 509 | 510 | 511 | 512 |
513 | 514 | 515 | 527 |
528 | 529 | 530 | 531 | 532 | 533 | 534 | 535 | 536 | 537 |
543 |
544 |
545 | 546 | 547 | 548 | 549 | 550 | 551 | 552 | 553 | 554 | 555 | 556 | 557 | 558 | 559 | 560 |
561 | 562 |
563 | 564 |
565 |

566 | 567 | 570 | / 571 | 572 | serde 573 | 574 | 575 |

576 | 577 | 578 |
579 | 580 | 613 | 614 |
615 | 616 | 706 |
707 | 708 | 709 |
710 |
711 | 712 | 713 | 714 | 715 | 716 |
717 | 718 | 719 | 720 | Permalink 721 | 722 | 723 | 724 |
725 | 726 |
727 |
728 | 731 | 732 | master 733 | 734 | 735 | 736 |
737 |
738 |
739 | Switch branches/tags 740 | 741 |
742 | 743 | 744 | 745 |
746 | 757 |
758 | 759 |
760 | 761 | 762 |
763 | 764 |
765 | 780 | 781 | 784 | 785 | 786 | 796 | 797 | 804 | 805 | 806 | 807 |
808 | 809 | 849 |
850 |
851 |
852 |
853 | 854 |
855 | 856 |
857 | 858 | 861 | 865 | Go to file 866 | 867 | 868 |
869 | 870 | 871 |
899 |
900 |
901 | 902 | 903 | 904 |
905 | 906 |
907 |
908 | 909 | @dtolnay 910 | 911 |
912 |
913 | dtolnay 914 | 915 | 916 | Copyright/license headers 929 | 930 |
931 | 932 | 933 | 934 | 935 | 936 | 937 | 938 | 939 |
940 |
941 | 942 | Latest commit 943 | 58b3af4 944 | Nov 25, 2018 945 | 946 | 947 | 948 | 949 | 950 | History 951 | 952 | 953 |
954 |
955 |
The following changes are included:
 956 | 
 957 | - Delete per-file license notices at the top of each file.
 958 | - Delete the first paragraph of LICENSE-MIT (an inaccurate
 959 |   pseudo-copyright line), leaving only the text of the MIT license.
 960 | 
 961 | Nothing about the license of Serde code has changed, only our
 962 | understanding of how to correctly communicate that license has changed.
 963 | 
 964 | This mirrors an equivalent change being applied in the rust-lang/rust
 965 | repository.
966 | 967 |
968 | 969 |
970 |
971 | 972 | 973 | 2 974 | 975 | contributors 976 | 977 | 981 |
982 | 985 |

986 | Users who have contributed to this file 987 |

988 |
989 | 990 | 991 | 992 | 993 | 994 | 995 | 996 | 997 |
998 |
999 | 1000 | 1001 | @erickt 1002 | 1003 | @dtolnay 1004 | 1005 | 1006 | 1007 |
1008 |
1009 | 1010 | 1011 | 1012 | 1013 | 1014 | 1015 | 1016 |
1018 | 1019 |
1020 |
1021 | 1022 | 23 lines (21 sloc) 1023 | 1024 | 1023 Bytes 1025 |
1026 | 1027 |
1028 | 1029 |
1030 | Raw 1031 | Blame 1032 |
1033 | 1034 |
1035 | 1040 | 1041 | 1042 | 1043 | 1045 | 1046 | 1047 | 1049 | 1050 | 1051 |
1052 |
1053 |
1054 | 1055 | 1056 | 1057 |
1058 | 1059 | 1060 | 1061 | 1062 | 1063 | 1064 | 1065 | 1066 | 1067 | 1068 | 1069 | 1070 | 1071 | 1072 | 1073 | 1074 | 1075 | 1076 | 1077 | 1078 | 1079 | 1080 | 1081 | 1082 | 1083 | 1084 | 1085 | 1086 | 1087 | 1088 | 1089 | 1090 | 1091 | 1092 | 1093 | 1094 | 1095 | 1096 | 1097 | 1098 | 1100 | 1101 | 1102 | 1103 | 1104 | 1105 | 1106 | 1107 | 1108 | 1109 | 1110 | 1111 | 1112 | 1113 | 1114 | 1115 | 1117 | 1118 | 1119 | 1120 | 1121 | 1122 | 1123 | 1124 | 1125 | 1126 | 1127 | 1128 | 1129 | 1130 | 1131 | 1132 | 1133 | 1134 | 1135 | 1136 | 1137 | 1138 | 1139 | 1140 | 1141 | 1142 | 1143 | 1144 | 1145 | 1146 | 1147 | 1148 | 1149 | 1150 | 1151 | 1152 | 1153 | 1154 |
Permission is hereby granted, free of charge, to any
person obtaining a copy of this software and associated
documentation files (the "Software"), to deal in the
Software without restriction, including without
limitation the rights to use, copy, modify, merge,
publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software
is furnished to do so, subject to the following
conditions:
1099 |
The above copyright notice and this permission notice
shall be included in all copies or substantial portions
of the Software.
1116 |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
1155 | 1156 | 1177 | 1178 |
1179 | 1180 |
1181 | 1182 | 1183 | 1184 |
1185 | 1186 | 1187 |
1188 | 1189 | 1190 |
1191 |
1192 | 1193 | 1194 |
1195 | 1196 | 1197 | 1198 |
1199 |
1200 | 1201 |
1202 |
1203 | 1204 | 1205 |
1206 | 1207 | 1208 | 1237 | 1238 | 1239 | 1240 | 1247 | 1248 | 1254 | 1265 | 1266 | 1270 | 1271 | 1272 | 1273 | 1274 | 1275 | 1276 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | escher/README.md -------------------------------------------------------------------------------- /assets/almost-foo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petrosagg/escher/d0e66f0454b7612cde35c11546b2ecd6feffc9e8/assets/almost-foo.png -------------------------------------------------------------------------------- /assets/async-stack.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petrosagg/escher/d0e66f0454b7612cde35c11546b2ecd6feffc9e8/assets/async-stack.png -------------------------------------------------------------------------------- /assets/moves-invalidate-pointer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/petrosagg/escher/d0e66f0454b7612cde35c11546b2ecd6feffc9e8/assets/moves-invalidate-pointer.png -------------------------------------------------------------------------------- /escher-derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "escher-derive" 3 | version = "0.2.0" 4 | authors = ["Petros Angelatos "] 5 | license = "MIT OR Apache-2.0" 6 | description = "Self-referencial structs using the async/await transformation" 7 | repository = "https://github.com/petrosagg/escher" 8 | keywords = ["lifetime", "ownership", "borrowing", "self", "reference"] 9 | documentation = "https://docs.rs/escher" 10 | readme = "README.md" 11 | edition = "2018" 12 | 13 | [lib] 14 | proc-macro = true 15 | 16 | [dependencies] 17 | syn = "1.0" 18 | quote = "1.0" 19 | -------------------------------------------------------------------------------- /escher-derive/README.md: -------------------------------------------------------------------------------- 1 | # escher 2 | 3 | > Self-referencial structs using async stacks 4 | 5 | Escher is an extremely simple library providing a safe and sound API to build 6 | self-referencial structs. It works by (ab)using the async await trasformation 7 | of rustc. If you'd like to know more about the inner workings please take a 8 | look at the [How it works](#how-it-works) section and the source code. 9 | 10 | Compared to the state of the art escher: 11 | 12 | * Is only around 100 lines of well-commented code 13 | * Contains only two `unsafe` calls that are well argued for 14 | * Uses rustc for all the analysis. If it compiles, the self references are correct 15 | 16 | ## Usage 17 | 18 | You are looking at the escher-derive crate. You can find the full documentation 19 | in the main escher crate. 20 | -------------------------------------------------------------------------------- /escher-derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::{TokenStream}; 2 | use quote::{quote, ToTokens}; 3 | use syn::{parse_macro_input, DeriveInput}; 4 | 5 | #[proc_macro_derive(Rebindable)] 6 | pub fn derive_rebindable(input: TokenStream) -> TokenStream { 7 | let input = parse_macro_input!(input as DeriveInput); 8 | 9 | let name = input.ident; 10 | let generics = input.generics; 11 | 12 | let mut impl_params: Vec> = vec![Box::new(quote! { 'a })]; 13 | let mut type_params: Vec> = vec![]; 14 | let mut out_params: Vec> = vec![]; 15 | 16 | for _ in generics.lifetimes() { 17 | type_params.push(Box::new(quote! { '_ })); 18 | out_params.push(Box::new(quote! { 'a })); 19 | } 20 | 21 | for ident in generics.type_params().map(|p| &p.ident) { 22 | impl_params.push(Box::new(quote! { #ident: 'a })); 23 | type_params.push(Box::new(ident.clone())); 24 | out_params.push(Box::new(ident.clone())); 25 | } 26 | 27 | for param in generics.const_params() { 28 | let ident = ¶m.ident; 29 | let ty = ¶m.ty; 30 | impl_params.push(Box::new(quote! { const #ident: #ty })); 31 | type_params.push(Box::new(ident.clone())); 32 | out_params.push(Box::new(ident.clone())); 33 | } 34 | 35 | TokenStream::from(quote! { 36 | unsafe impl<#(#impl_params),*> escher::RebindTo<'a> for #name<#(#type_params),*> { 37 | type Out = #name<#(#out_params),*>; 38 | } 39 | 40 | impl escher::Rebindable for #name<#(#type_params),*> { 41 | fn rebind<'short, 'long: 'short>(&'long self) -> &'short escher::Rebind<'short, Self> 42 | where Self: 'long 43 | { 44 | self 45 | } 46 | } 47 | }) 48 | } 49 | -------------------------------------------------------------------------------- /escher/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "escher" 3 | version = "0.3.0" 4 | authors = ["Petros Angelatos "] 5 | license = "MIT OR Apache-2.0" 6 | description = "Self-referencial structs using the async/await transformation" 7 | repository = "https://github.com/petrosagg/escher" 8 | keywords = ["lifetime", "ownership", "borrowing", "self", "reference"] 9 | documentation = "https://docs.rs/escher" 10 | readme = "README.md" 11 | edition = "2018" 12 | exclude = [ 13 | "assets/*", 14 | ] 15 | 16 | 17 | [dependencies] 18 | escher-derive = { version = "0.2.0", path = "../escher-derive" } 19 | futures-task = "0.3" 20 | -------------------------------------------------------------------------------- /escher/README.md: -------------------------------------------------------------------------------- 1 | [![Crates.io](https://img.shields.io/crates/v/escher.svg)](https://crates.io/crates/escher) 2 | 3 | # escher 4 | 5 | > Self-referencial structs using async stacks 6 | 7 | Escher is an extremely simple library providing a safe and sound API to build 8 | self-referencial structs. It works by (ab)using the async await trasformation 9 | of rustc. If you'd like to know more about the inner workings please take a 10 | look at the [How it works](#how-it-works) section and the source code. 11 | 12 | Compared to the state of the art escher: 13 | 14 | * Is only around 100 lines of well-commented code 15 | * Contains only two `unsafe` calls that are well argued for 16 | * Uses rustc for all the analysis. If it compiles, the self references are correct 17 | 18 | ## Usage 19 | 20 | This library provides the [Escher](Escher) wrapper type that can hold self-referencial data 21 | and expose them safely through the [as_ref()](Escher::as_ref) and [as_mut()](Escher::as_mut) 22 | functions. 23 | 24 | You construct a self reference by calling Escher's constructor and providing an async closure 25 | that will initialize your self-references on its stack. Your closure will be provided with a 26 | capturer `r` that has a single [capture()](Capturer::capture) method that consumes `r`. 27 | 28 | > **Note:** It is important to `.await` the result `.capture()` in order for escher to correctly 29 | initialize your struct. 30 | 31 | Once all the data and references are created you can capture the desired ones. Simple 32 | references to owned data can be captured directly (see first example). 33 | 34 | To capture more than one variable or capture references to non-owned data you will have to 35 | define your own reference struct that derives [Rebindable](escher_derive::Rebindable) (see 36 | second example). 37 | 38 | ## Examples 39 | 40 | ### Simple `&str` view into an owned `Vec` 41 | 42 | The simplest way to use Escher is to create a reference of some data and then capture it: 43 | 44 | ```rust 45 | use escher::Escher; 46 | 47 | let escher_heart = Escher::new(|r| async move { 48 | let data: Vec = vec![240, 159, 146, 150]; 49 | let sparkle_heart = std::str::from_utf8(&data).unwrap(); 50 | 51 | r.capture(sparkle_heart).await; 52 | }); 53 | 54 | assert_eq!("💖", *escher_heart.as_ref()); 55 | ``` 56 | 57 | ### Capturing both a `Vec` and a `&str` view into it 58 | 59 | In order to capture more than one things you can define a struct that will be used to capture 60 | the variables: 61 | 62 | ```rust 63 | use escher::{Escher, Rebindable}; 64 | 65 | #[derive(Rebindable)] 66 | struct VecStr<'this> { 67 | data: &'this Vec, 68 | s: &'this str, 69 | } 70 | 71 | let escher_heart = Escher::new(|r| async move { 72 | let data: Vec = vec![240, 159, 146, 150]; 73 | 74 | r.capture(VecStr{ 75 | data: &data, 76 | s: std::str::from_utf8(&data).unwrap(), 77 | }).await; 78 | }); 79 | 80 | assert_eq!(240, escher_heart.as_ref().data[0]); 81 | assert_eq!("💖", escher_heart.as_ref().s); 82 | ``` 83 | 84 | ### Capturing a mutable `&mut str` view into a `Vec` 85 | 86 | If you capture a mutable reference to some piece of data then you cannot capture the data 87 | itself like the previous example. This is mandatory as doing otherwise would create two mutable 88 | references into the same piece of data which is not allowed. 89 | 90 | ```rust 91 | use escher::Escher; 92 | 93 | let mut name = Escher::new(|r| async move { 94 | let mut data: Vec = vec![101, 115, 99, 104, 101, 114]; 95 | let name = std::str::from_utf8_mut(&mut data).unwrap(); 96 | 97 | r.capture(name).await; 98 | }); 99 | 100 | assert_eq!("escher", *name.as_ref()); 101 | name.as_mut().make_ascii_uppercase(); 102 | assert_eq!("ESCHER", *name.as_ref()); 103 | ``` 104 | 105 | ### Capturing multiple mixed references 106 | 107 | ```rust 108 | use escher::{Escher, Rebindable}; 109 | 110 | #[derive(Rebindable)] 111 | struct MyStruct<'this> { 112 | int_data: &'this Box, 113 | int_ref: &'this i32, 114 | float_ref: &'this mut f32, 115 | } 116 | 117 | let mut my_value = Escher::new(|r| async move { 118 | let int_data = Box::new(42); 119 | let mut float_data = Box::new(3.14); 120 | 121 | r.capture(MyStruct{ 122 | int_data: &int_data, 123 | int_ref: &int_data, 124 | float_ref: &mut float_data, 125 | }).await; 126 | }); 127 | 128 | assert_eq!(Box::new(42), *my_value.as_ref().int_data); 129 | assert_eq!(3.14, *my_value.as_ref().float_ref); 130 | 131 | *my_value.as_mut().float_ref = (*my_value.as_ref().int_ref as f32) * 2.0; 132 | 133 | assert_eq!(84.0, *my_value.as_ref().float_ref); 134 | ``` 135 | 136 | ## How it works 137 | 138 | ### The problem with self-references 139 | 140 | The main problem with self-referencial structs is that if such a struct was 141 | somehow constructed the compiler would then have to statically prove that it 142 | would not move again. This analysis is necessary because any move would 143 | invalidate self-pointers since all pointers in rust are absolute memory 144 | addresses. 145 | 146 | To illustrate why this is necessary, imagine we define a self-referencial 147 | struct that holds a Vec and a pointer to it at the same time: 148 | 149 | ```rust 150 | struct Foo { 151 | s: Vec, 152 | p: &Vec, 153 | } 154 | ``` 155 | 156 | Then, let's assume we had a way of getting an instance of this struct. We could 157 | then write the following code that creates a dangling pointer in safe rust! 158 | 159 | ```rust 160 | let foo = Foo::magic_construct(); 161 | 162 | let bar = foo; // move foo to a new location 163 | println!("{:?}", bar.p); // access the self-reference, memory error! 164 | ``` 165 |

166 | 167 |

168 | 169 | ### Almost-self-references on the stack 170 | 171 | While rust doesn't allow you to explicitly write out self referencial struct 172 | members and initialize them it is perfectly valid to write out the values of 173 | the members individually as separate stack bindings. This is because the borrow 174 | checker *can* do a move analysis when the values are on the stack. 175 | 176 | Practically, we could convert the struct `Foo` from above to individual 177 | bindings like so: 178 | 179 | ```rust 180 | fn foo() { 181 | let s = vec![1, 2, 3]; 182 | let p = &s; 183 | } 184 | ``` 185 | 186 | Then, we could wrap both of them in a struct that only has references and use that instead: 187 | 188 | ```rust 189 | struct AlmostFoo<'a> { 190 | s: &'a Vec, 191 | p: &'a Vec, 192 | } 193 | 194 | fn make_foo() { 195 | let s = vec![1, 2, 3]; 196 | let p = &s; 197 | 198 | let foo = AlmostFoo { s, p }; 199 | 200 | do_stuff(foo); // call a function that expects an AlmostFoo 201 | } 202 | ``` 203 | 204 | Of course `make_foo()` cannot return an `AlmostFoo` instance since it would be 205 | referencing values from its stack, but what it can do is call other functions 206 | and pass an `AlmostFoo` to them. In other words, as long as the code that wants 207 | to use `AlmostFoo` is above `make_foo()` we can use this technique and work 208 | with almost-self-references. 209 | 210 |

211 | 212 |

213 | 214 | 215 | This is pretty restrictive though. Ideally we'd lke to be able return some 216 | owned value and be free to move it around, put it on the heap, etc. 217 | 218 | ### Actually returning an `AlmostFoo` 219 | 220 | > **Note:** The description of async stacks bellow is not what actually happens 221 | > in rustc but is enough to illustrate the point. `escher`'s API does make use 222 | > that the desired values are held across an await point to force them to be 223 | > included in the generated Future. 224 | 225 | As we saw, it is impossible to return an `AlmostFoo` instance since it 226 | references values from the stack. But what if we could freeze the stack after 227 | an `AlmostFoo` instance got constructed and then returned the whole stack? 228 | 229 | Well, there is no way for a regular function to capture its own stack and 230 | return it but that is exactly what the async/await transformation does! Let's 231 | make `make_foo` from above async and also make it never terminate: 232 | 233 | ```rust 234 | async fn make_foo() { 235 | let s = vec![1, 2, 3]; 236 | let p = &s; 237 | let foo = AlmostFoo { s, p }; 238 | std::future::pending().await 239 | } 240 | ``` 241 | 242 | Now when someone calls `make_foo()` what they get back is some struct that 243 | implements Future. This struct is in fact a representation of the stack of 244 | `make_foo` at its initial state, i.e in the state that the function has not be 245 | called yet. 246 | 247 | What we need to do now is to step the execution of the returned Future until 248 | the instance of `AlmostFoo` is constructed. In this case we know that there is 249 | a single await point so we only need to poll the Future once. Before we do that 250 | though we need to put it in a Pinned Box to ensure that as we poll the future 251 | no moves will occur. This is the same restriction as with normal function but 252 | with async it is enforced using the `Pin

` type. 253 | 254 | ```rust 255 | let foo = make_foo(); // construct a stack that will eventually make an AlmostFoo in it 256 | let mut foo = Box::pin(foo_fut); // pin it so that it never moves again 257 | foo.poll(); // poll it once 258 | 259 | // now we know that somewhere inside `foo` there is a valid AlmostFoo instance! 260 | ``` 261 | 262 | We're almost there! We now have an owned value, the future, that somewhere 263 | inside it has an AlmostFoo instance. However we have no way of retrieving the 264 | exact memory location of it or accessing it in any way. The Future is opaque. 265 | 266 |

267 | 268 |

269 | 270 | ### Putting it all together 271 | 272 | `escher` builds upon the techniques described above and provides a solution for 273 | getting the pointer from within the opaque future struct. Each `Escher` 274 | instance holds a Pinned Future and a raw pointer to T. The pointer to T is 275 | computed by polling the Future just enough times for the desired T to be 276 | constructed. 277 | 278 | As its API, it provides the `as_ref()` and `as_mut()` methods that unsafely 279 | turn the raw pointer to T into a &T with its lifetime bound to the lifetime of 280 | `Escher` itself. This ensures that the future will outlive any usage of the 281 | self-reference! 282 | 283 | Thank you for reading this far! If you would like to learn how escher uses the 284 | above concepts in detail please take a look at the implementation. 285 | 286 | ## License 287 | 288 | Licensed under either of 289 | 290 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) 291 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT) 292 | 293 | at your option. 294 | 295 | ### Contribution 296 | 297 | Unless you explicitly state otherwise, any contribution intentionally 298 | submitted for inclusion in the work by you, as defined in the Apache-2.0 299 | license, shall be dual licensed as above, without any additional terms or 300 | conditions. 301 | -------------------------------------------------------------------------------- /escher/README.tpl: -------------------------------------------------------------------------------- 1 | [![Crates.io](https://img.shields.io/crates/v/escher.svg)](https://crates.io/crates/escher) 2 | 3 | # {{crate}} 4 | 5 | {{readme}} 6 | 7 | ## How it works 8 | 9 | ### The problem with self-references 10 | 11 | The main problem with self-referencial structs is that if such a struct was 12 | somehow constructed the compiler would then have to statically prove that it 13 | would not move again. This analysis is necessary because any move would 14 | invalidate self-pointers since all pointers in rust are absolute memory 15 | addresses. 16 | 17 | To illustrate why this is necessary, imagine we define a self-referencial 18 | struct that holds a Vec and a pointer to it at the same time: 19 | 20 | ```rust 21 | struct Foo { 22 | s: Vec, 23 | p: &Vec, 24 | } 25 | ``` 26 | 27 | Then, let's assume we had a way of getting an instance of this struct. We could 28 | then write the following code that creates a dangling pointer in safe rust! 29 | 30 | ```rust 31 | let foo = Foo::magic_construct(); 32 | 33 | let bar = foo; // move foo to a new location 34 | println!("{:?}", bar.p); // access the self-reference, memory error! 35 | ``` 36 | 37 | ![Moves invalidate pointer](https://github.com/petrosagg/escher/blob/master/assets/moves-invalidate-pointer.png?raw=true) 38 | 39 | ### Almost-self-references on the stack 40 | 41 | While rust doesn't allow you to explicitly write out self referencial struct 42 | members and initialize them it is perfectly valid to write out the values of 43 | the members individually as separate stack bindings. This is because the borrow 44 | checker *can* do a move analysis when the values are on the stack. 45 | 46 | Practically, we could convert the struct `Foo` from above to individual 47 | bindings like so: 48 | 49 | ```rust 50 | fn foo() { 51 | let s = vec![1, 2, 3]; 52 | let p = &s; 53 | } 54 | ``` 55 | 56 | Then, we could wrap both of them in a struct that only has references and use that instead: 57 | 58 | ```rust 59 | struct AlmostFoo<'a> { 60 | s: &'a Vec, 61 | p: &'a Vec, 62 | } 63 | 64 | fn make_foo() { 65 | let s = vec![1, 2, 3]; 66 | let p = &s; 67 | 68 | let foo = AlmostFoo { s, p }; 69 | 70 | do_stuff(foo); // call a function that expects an AlmostFoo 71 | } 72 | ``` 73 | 74 | Of course `make_foo()` cannot return an `AlmostFoo` instance since it would be 75 | referencing values from its stack, but what it can do is call other functions 76 | and pass an `AlmostFoo` to them. In other words, as long as the code that wants 77 | to use `AlmostFoo` is above `make_foo()` we can use this technique and work 78 | with almost-self-references. 79 | 80 | ![Almost self-reference](https://github.com/petrosagg/escher/blob/master/assets/almost-foo.png?raw=true) 81 | 82 | This is pretty restrictive though. Ideally we'd lke to be able return some 83 | owned value and be free to move it around, put it on the heap, etc. 84 | 85 | ### Actually returning an `AlmostFoo` 86 | 87 | > **Note:** The description of async stacks bellow is not what actually happens 88 | > in rustc but is enough to illustrate the point. `escher`'s API does make use 89 | > that the desired values are held across an await point to force them to be 90 | > included in the generated Future. 91 | 92 | As we saw, it is impossible to return an `AlmostFoo` instance since it 93 | references values from the stack. But what if we could freeze the stack after 94 | an `AlmostFoo` instance got constructed and then returned the whole stack? 95 | 96 | Well, there is no way for a regular function to capture its own stack and 97 | return it but that is exactly what the async/await transformation does! Let's 98 | make `make_foo` from above async and also make it never terminate: 99 | 100 | ```rust 101 | async fn make_foo() { 102 | let s = vec![1, 2, 3]; 103 | let p = &s; 104 | let foo = AlmostFoo { s, p }; 105 | std::future::pending().await 106 | } 107 | ``` 108 | 109 | Now when someone calls `make_foo()` what they get back is some struct that 110 | implements Future. This struct is in fact a representation of the stack of 111 | `make_foo` at its initial state, i.e in the state that the function has not be 112 | called yet. 113 | 114 | What we need to do now is to step the execution of the returned Future until 115 | the instance of `AlmostFoo` is constructed. In this case we know that there is 116 | a single await point so we only need to poll the Future once. Before we do that 117 | though we need to put it in a Pinned Box to ensure that as we poll the future 118 | no moves will occur. This is the same restriction as with normal function but 119 | with async it is enforced using the `Pin

` type. 120 | 121 | ```rust 122 | let foo = make_foo(); // construct a stack that will eventually make an AlmostFoo in it 123 | let mut foo = Box::pin(foo_fut); // pin it so that it never moves again 124 | foo.poll(); // poll it once 125 | 126 | // now we know that somewhere inside `foo` there is a valid AlmostFoo instance! 127 | ``` 128 | 129 | We're almost there! We now have an owned value, the future, that somewhere 130 | inside it has an AlmostFoo instance. However we have no way of retrieving the 131 | exact memory location of it or accessing it in any way. The Future is opaque. 132 | 133 | ![Async stack](https://github.com/petrosagg/escher/blob/master/assets/async-stack.png?raw=true) 134 | 135 | ### Putting it all together 136 | 137 | `escher` builds upon the techniques described above and provides a solution for 138 | getting the pointer from within the opaque future struct. Each `Escher` 139 | instance holds a Pinned Future and a raw pointer to T. The pointer to T is 140 | computed by polling the Future just enough times for the desired T to be 141 | constructed. 142 | 143 | As its API, it provides the `as_ref()` and `as_mut()` methods that unsafely 144 | turn the raw pointer to T into a &T with its lifetime bound to the lifetime of 145 | `Escher` itself. This ensures that the future will outlive any usage of the 146 | self-reference! 147 | 148 | Thank you for reading this far! If you would like to learn how escher uses the 149 | above concepts in detail please take a look at the implementation. 150 | 151 | ## License 152 | 153 | Licensed under either of 154 | 155 | * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or https://www.apache.org/licenses/LICENSE-2.0) 156 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or https://opensource.org/licenses/MIT) 157 | 158 | at your option. 159 | 160 | ### Contribution 161 | 162 | Unless you explicitly state otherwise, any contribution intentionally 163 | submitted for inclusion in the work by you, as defined in the Apache-2.0 164 | license, shall be dual licensed as above, without any additional terms or 165 | conditions. 166 | -------------------------------------------------------------------------------- /escher/src/escher.rs: -------------------------------------------------------------------------------- 1 | use std::future::Future; 2 | use std::pin::Pin; 3 | use std::ptr::NonNull; 4 | use std::sync::atomic::{AtomicPtr, Ordering}; 5 | use std::sync::Arc; 6 | use std::task::Context; 7 | 8 | use futures_task::noop_waker; 9 | 10 | /// The `RebindTo` trait defines a type level function that allows you convert a type that holds 11 | /// references of lifetime `'a` to a type that holds references of lifetime `'b`. 12 | /// 13 | /// The trait is unsafe because the implementer needs to make sure that the associated type 14 | /// differs with the implementing type only on their lifetimes. In other words, it's meant to 15 | /// prevent incantations like: 16 | /// 17 | /// ```ignore 18 | /// unsafe impl<'a> RebindTo<'a> for Foo<'_> { 19 | /// type Out = Bar<'a>; // !!WRONG!! 20 | /// } 21 | /// 22 | /// unsafe impl<'a> RebindTo<'a> for Foo<'_> { 23 | /// type Out = Foo<'a>; // CORRECT 24 | /// } 25 | /// ``` 26 | /// 27 | /// Users should avoid implementing this trait manually and derive 28 | /// [Rebindable](escher_derive::Rebindable) instead. 29 | pub unsafe trait RebindTo<'a> { 30 | type Out: 'a; 31 | } 32 | 33 | /// Blanket implementation for any reference to owned data 34 | unsafe impl<'a, T: ?Sized + 'static> RebindTo<'a> for &'_ T { 35 | type Out = &'a T; 36 | } 37 | 38 | /// Blanket implementation for any mutable reference to owned data 39 | unsafe impl<'a, T: ?Sized + 'static> RebindTo<'a> for &'_ mut T { 40 | type Out = &'a mut T; 41 | } 42 | 43 | /// Marker trait for any type that implements [RebindTo] for any lifetime. All types can derive 44 | /// this trait using the [Rebindable](escher_derive::Rebindable) derive macro. 45 | pub trait Rebindable: for<'a> RebindTo<'a> { 46 | fn rebind<'short, 'long: 'short>(&'long self) -> &'short Rebind<'short, Self> 47 | where Self: 'long; 48 | } 49 | 50 | /// Type-level function that takes a lifetime `'a` and a type `T` computes a new type `U` that is 51 | /// identical to `T` except for its lifetimes that are now bound to `'a`. 52 | /// 53 | /// A type `T` must implement [Rebindable] in order to use this type level function. 54 | /// 55 | /// For example: 56 | /// 57 | /// * `Rebind<'a, &'static str> == &'a str` 58 | /// * `Rebind<'static, &'a str> == &'static str` 59 | /// * `Rebind<'c, T<'a, 'b>> == T<'c, 'c>` 60 | pub type Rebind<'a, T> = >::Out; 61 | 62 | /// A containter of a self referencial struct. The self-referencial struct is constructed with the 63 | /// aid of the async/await machinery of rustc, see [Escher::new]. 64 | pub struct Escher<'fut, T> { 65 | _fut: Pin + 'fut>>, 66 | ptr: NonNull, 67 | } 68 | 69 | impl<'fut, T: Rebindable> Escher<'fut, T> { 70 | /// Construct a self referencial struct using the provided closure. The user is expected to 71 | /// construct the desired data and references to them in the async stack and capture the 72 | /// desired state when ready. 73 | /// 74 | /// ```rust 75 | /// use escher::{Escher, Rebindable}; 76 | /// 77 | /// #[derive(Rebindable)] 78 | /// struct MyStr<'a>(&'a str); 79 | /// 80 | /// let escher_heart = Escher::new(|r| async move { 81 | /// let data: Vec = vec![240, 159, 146, 150]; 82 | /// let sparkle_heart = std::str::from_utf8(&data).unwrap(); 83 | /// 84 | /// r.capture(MyStr(sparkle_heart)).await; 85 | /// }); 86 | /// 87 | /// assert_eq!("💖", escher_heart.as_ref().0); 88 | /// ``` 89 | pub fn new(builder: B) -> Self 90 | where 91 | B: FnOnce(Capturer) -> F, 92 | F: Future + 'fut, 93 | { 94 | let ptr = Arc::new(AtomicPtr::new(std::ptr::null_mut())); 95 | let r = Capturer { ptr: ptr.clone() }; 96 | let mut fut = Box::pin(builder(r)); 97 | 98 | let waker = noop_waker(); 99 | let mut cx = Context::from_waker(&waker); 100 | let _ = fut.as_mut().poll(&mut cx); 101 | 102 | // Adversarial code can attempt to capture a value without awaiting on the result 103 | assert!( 104 | Arc::strong_count(&ptr) == 2, 105 | "capture no longer live. Did you forget to .await the result of capture()?" 106 | ); 107 | 108 | let ptr = ptr.load(Ordering::Acquire); 109 | 110 | let low = &*fut as *const _ as usize; 111 | let high = low + std::mem::size_of_val(&*fut); 112 | // Adversarial code can attempt to capture a value that does not live on the async stack 113 | assert!( 114 | low <= ptr as usize && ptr as usize <= high, 115 | "captured value outside of async stack. Did you run capture() in a non async function?" 116 | ); 117 | 118 | // SAFETY: At this point we know that: 119 | // 2. We have a pointer that points into the state of the future 120 | // 3. The state of the future will never move again because it's behind a Pin> 121 | // 4. The pointer `ptr` points to a valid instance of T because: 122 | // a. T will be kept alive as long as the future is kept alive, and we own it 123 | // b. The only way to set the pointer is through Capturer::capture that expects a T 124 | // c. The strong count of AtomicPtr is 2, so the async stack is in Capturer::capture_ref because: 125 | // α. Capturer is not Clone, so one cannot fake the increased refcount 126 | // β. Capturer::capture consumes Capturer so when the function returns the Arc will be dropped 127 | Escher { 128 | _fut: fut, 129 | ptr: unsafe { NonNull::new_unchecked(ptr) }, 130 | } 131 | } 132 | 133 | /// Get a shared reference to the inner `T` with its lifetime bound to `&self` 134 | pub fn as_ref<'a>(&'a self) -> &Rebind<'a, T> { 135 | // SAFETY 136 | // Validity of reference 137 | // self.ptr points to a valid instance of T in side of self._fut (see safety argument in 138 | // constructor) 139 | // Liveness of reference 140 | // The resulting reference is has all its lifetimes bound to the lifetime of self that 141 | // contains _fut that contains all the data that ptr could be referring to because it's 142 | // a 'static Future 143 | unsafe { (&*self.ptr.as_ptr()).rebind() } 144 | } 145 | 146 | /// Get a mut reference to the inner `T` with its lifetime bound to `&mut self` 147 | pub fn as_mut<'a>(&'a mut self) -> &mut Rebind<'a, T> { 148 | unimplemented!() 149 | } 150 | } 151 | 152 | /// An instance of `Capturer` is given to the closure passed to [Escher::new] and is used to 153 | /// capture a reference from the async stack. 154 | pub struct Capturer { 155 | ptr: Arc>, 156 | } 157 | 158 | impl Capturer { 159 | async fn capture_ref(self, val: &mut T) 160 | where 161 | // once rustc supports equality constraints this can become: `StaticT = Rebind<'static, T>` 162 | T: RebindTo<'static, Out = StaticT>, 163 | { 164 | self.ptr 165 | .store(val as *mut _ as *mut StaticT, Ordering::Release); 166 | std::future::pending::<()>().await; 167 | } 168 | 169 | /// Captures the passed value into a future that never resolves. 170 | /// Callers of this method **must** `.await` it in order for Escher to capture the value. 171 | pub async fn capture(self, mut val: T) 172 | where 173 | // once rustc supports equality constraints this can become: `StaticT = Rebind<'static, T>` 174 | T: RebindTo<'static, Out = StaticT>, 175 | { 176 | self.capture_ref(&mut val).await; 177 | } 178 | } 179 | -------------------------------------------------------------------------------- /escher/src/lib.rs: -------------------------------------------------------------------------------- 1 | //! > Self-referencial structs using async stacks 2 | //! 3 | //! Escher is an extremely simple library providing a safe and sound API to build self-referencial 4 | //! structs. It works by (ab)using the async await trasformation of rustc. If you'd like to know 5 | //! more about the inner workings please take a look at the [How it 6 | //! works](https://github.com/petrosagg/escher#how-it-works) section and the source code. 7 | //! 8 | //! Compared to the state of the art escher: 9 | //! 10 | //! * Is only around 100 lines of well-commented code 11 | //! * Contains only two `unsafe` calls that are well argued for 12 | //! * Uses rustc for all the analysis. If it compiles, the self references are correct 13 | //! 14 | //! # Usage 15 | //! 16 | //! This library provides the [Escher](Escher) wrapper type that can hold self-referencial data 17 | //! and expose them safely through the [as_ref()](Escher::as_ref) and [as_mut()](Escher::as_mut) 18 | //! functions. 19 | //! 20 | //! You construct a self reference by calling Escher's constructor and providing an async closure 21 | //! that will initialize your self-references on its stack. Your closure will be provided with a 22 | //! capturer `r` that has a single [capture()](Capturer::capture) method that consumes `r`. 23 | //! 24 | //! > **Note:** It is important to `.await` the result `.capture()` in order for escher to correctly 25 | //! initialize your struct. 26 | //! 27 | //! Once all the data and references are created you can capture the desired ones. Simple 28 | //! references to owned data can be captured directly (see first example). 29 | //! 30 | //! To capture more than one variable or capture references to non-owned data you will have to 31 | //! define your own reference struct that derives [Rebindable](escher_derive::Rebindable) (see 32 | //! second example). 33 | //! 34 | //! # Examples 35 | //! 36 | //! ## Simple `&str` view into an owned `Vec` 37 | //! 38 | //! The simplest way to use Escher is to create a reference of some data and then capture it: 39 | //! 40 | //! ```rust 41 | //! use escher::{Escher, Rebindable}; 42 | //! 43 | //! #[derive(Rebindable)] 44 | //! struct MyStr<'a>(&'a str); 45 | //! 46 | //! let escher_heart = Escher::new(|r| async move { 47 | //! let data: Vec = vec![240, 159, 146, 150]; 48 | //! let sparkle_heart = std::str::from_utf8(&data).unwrap(); 49 | //! 50 | //! r.capture(MyStr(sparkle_heart)).await; 51 | //! }); 52 | //! 53 | //! assert_eq!("💖", escher_heart.as_ref().0); 54 | //! ``` 55 | //! 56 | //! ## Capturing both a `Vec` and a `&str` view into it 57 | //! 58 | //! In order to capture more than one things you can define a struct that will be used to capture 59 | //! the variables: 60 | //! 61 | //! ```rust 62 | //! use escher::{Escher, Rebindable}; 63 | //! 64 | //! #[derive(Rebindable)] 65 | //! struct VecStr<'this> { 66 | //! data: &'this Vec, 67 | //! s: &'this str, 68 | //! } 69 | //! 70 | //! let escher_heart = Escher::new(|r| async move { 71 | //! let data: Vec = vec![240, 159, 146, 150]; 72 | //! 73 | //! r.capture(VecStr{ 74 | //! data: &data, 75 | //! s: std::str::from_utf8(&data).unwrap(), 76 | //! }).await; 77 | //! }); 78 | //! 79 | //! assert_eq!(240, escher_heart.as_ref().data[0]); 80 | //! assert_eq!("💖", escher_heart.as_ref().s); 81 | //! ``` 82 | // 83 | // ## Capturing a mutable `&mut str` view into a `Vec` 84 | // 85 | // If you capture a mutable reference to some piece of data then you cannot capture the data 86 | // itself like the previous example. This is mandatory as doing otherwise would create two mutable 87 | // references into the same piece of data which is not allowed. 88 | // 89 | // ```rust 90 | // use escher::Escher; 91 | // 92 | // let mut name = Escher::new(|r| async move { 93 | // let mut data: Vec = vec![101, 115, 99, 104, 101, 114]; 94 | // let name = std::str::from_utf8_mut(&mut data).unwrap(); 95 | // 96 | // r.capture(name).await; 97 | // }); 98 | // 99 | // assert_eq!("escher", *name.as_ref()); 100 | // name.as_mut().make_ascii_uppercase(); 101 | // assert_eq!("ESCHER", *name.as_ref()); 102 | // ``` 103 | //! 104 | //! ## Capturing multiple mixed references 105 | //! 106 | //! ```rust 107 | //! use escher::{Escher, Rebindable}; 108 | //! 109 | //! #[derive(Rebindable)] 110 | //! struct MyStruct<'this> { 111 | //! int_data: &'this Box, 112 | //! int_ref: &'this i32, 113 | //! float_ref: &'this mut f32, 114 | //! } 115 | //! 116 | //! let mut my_value = Escher::new(|r| async move { 117 | //! let int_data = Box::new(42); 118 | //! let mut float_data = Box::new(3.14); 119 | //! 120 | //! r.capture(MyStruct{ 121 | //! int_data: &int_data, 122 | //! int_ref: &int_data, 123 | //! float_ref: &mut float_data, 124 | //! }).await; 125 | //! }); 126 | //! 127 | //! assert_eq!(Box::new(42), *my_value.as_ref().int_data); 128 | //! assert_eq!(3.14, *my_value.as_ref().float_ref); 129 | //! ``` 130 | 131 | mod escher; 132 | mod tests; 133 | 134 | pub use crate::escher::*; 135 | /// This trait can be derived for any struct, enum, or union to make its lifetimes rebindable and 136 | /// thus compatible with the [Rebind] type level function. 137 | /// 138 | /// ``` 139 | /// use escher::Rebindable; 140 | /// 141 | /// #[derive(Rebindable)] 142 | /// struct VecStr<'this> { 143 | /// data: &'this Vec, 144 | /// s: &'this str, 145 | /// } 146 | /// ``` 147 | pub use escher_derive::Rebindable; 148 | -------------------------------------------------------------------------------- /escher/src/tests.rs: -------------------------------------------------------------------------------- 1 | #![cfg(test)] 2 | use super::*; 3 | use crate as escher; 4 | 5 | // #[test] 6 | // fn test_dtonlay() { 7 | // use std::cell::Cell; 8 | // 9 | // #[derive(Rebindable)] 10 | // struct Struct<'a>(fn(&'a String)); 11 | // 12 | // fn main() { 13 | // static STRING: String = String::new(); 14 | // thread_local!(static CELL: Cell<&'static String> = Cell::new(&STRING)); 15 | // let escher = Escher::new(|r| async { 16 | // r.capture(Struct(|x| CELL.with(|cell| cell.set(x)))).await; 17 | // }); 18 | // let mut string = Ok(".".repeat(3)); 19 | // let f = escher.as_ref().0; 20 | // let s = string.as_ref().unwrap(); 21 | // f(s); 22 | // string = Err((s.as_ptr(), 100usize, 100usize)); 23 | // CELL.with(|cell| println!("{}", cell.get())); 24 | // string.unwrap_err(); 25 | // } 26 | // } 27 | 28 | #[test] 29 | #[should_panic(expected = "capture no longer live")] 30 | fn adversarial_sync_fn() { 31 | #[derive(Rebindable)] 32 | struct MyStr<'a>(&'a str); 33 | 34 | Escher::new(|r| { 35 | let data: Vec = vec![240, 159, 146, 150]; 36 | let sparkle_heart = std::str::from_utf8(&data).unwrap(); 37 | 38 | let _ = r.capture(MyStr(sparkle_heart)); 39 | 40 | // dummy future to satisfy escher 41 | std::future::ready(()) 42 | }); 43 | } 44 | 45 | #[test] 46 | #[should_panic(expected = "captured value outside of async stack")] 47 | fn adversarial_capture_non_stack() { 48 | #[derive(Rebindable)] 49 | struct MyStr<'a>(&'a str); 50 | 51 | Escher::new(|r| { 52 | let data: Vec = vec![240, 159, 146, 150]; 53 | let sparkle_heart = std::str::from_utf8(&data).unwrap(); 54 | 55 | let fut = r.capture(MyStr(sparkle_heart)); 56 | // make it appear as if capture is still alive 57 | std::mem::forget(fut); 58 | 59 | // dummy future to satisfy escher 60 | std::future::ready(()) 61 | }); 62 | } 63 | 64 | #[test] 65 | fn capture_enum() { 66 | /// Holds a vector and a str reference to the data of the vector 67 | #[derive(Rebindable, PartialEq, Debug)] 68 | enum MaybeStr<'a> { 69 | Some(&'a str), 70 | None, 71 | } 72 | 73 | let escher_heart = Escher::new(|r| async move { 74 | let data: Vec = vec![240, 159, 146, 150]; 75 | let sparkle_heart = std::str::from_utf8(&data).unwrap(); 76 | 77 | r.capture(MaybeStr::Some(sparkle_heart)).await; 78 | }); 79 | assert_eq!(MaybeStr::Some("💖"), *escher_heart.as_ref()); 80 | } 81 | 82 | #[test] 83 | fn capture_union() { 84 | /// Holds a vector and a str reference to the data of the vector 85 | #[derive(Rebindable)] 86 | union MaybeStr<'a> { 87 | some: &'a str, 88 | none: (), 89 | } 90 | 91 | let escher_heart = Escher::new(|r| async move { 92 | let data: Vec = vec![240, 159, 146, 150]; 93 | let sparkle_heart = std::str::from_utf8(&data).unwrap(); 94 | 95 | r.capture(MaybeStr { 96 | some: sparkle_heart, 97 | }) 98 | .await; 99 | }); 100 | 101 | unsafe { 102 | assert_eq!("💖", escher_heart.as_ref().some); 103 | } 104 | } 105 | 106 | #[test] 107 | fn it_works() { 108 | /// Holds a vector and a str reference to the data of the vector 109 | #[derive(Rebindable)] 110 | struct VecStr<'a> { 111 | data: &'a Vec, 112 | s: &'a str, 113 | } 114 | 115 | let escher_heart = Escher::new(|r| async move { 116 | let data: Vec = vec![240, 159, 146, 150]; 117 | let sparkle_heart = std::str::from_utf8(&data).unwrap(); 118 | 119 | r.capture(VecStr { 120 | data: &data, 121 | s: sparkle_heart, 122 | }) 123 | .await; 124 | }); 125 | 126 | assert_eq!(240, escher_heart.as_ref().data[0]); 127 | assert_eq!("💖", escher_heart.as_ref().s); 128 | } 129 | --------------------------------------------------------------------------------