├── .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 |
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.
1241 |
1242 |
1245 | You can’t perform that action at this time.
1246 |
1247 |
1248 |
1250 |
1251 | You signed in with another tab or window. Reload to refresh your session.
1252 | You signed out in another tab or window. Reload to refresh your session.
1253 |
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 | [](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 | [](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 | 
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 | 
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 | 
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