├── .gitignore ├── Cargo.toml ├── LICENSE.APACHE ├── LICENSE.MIT ├── README.md ├── derive ├── Cargo.toml └── src │ └── lib.rs ├── src ├── lib.rs ├── lower.rs └── tests.rs └── tests ├── config.rs ├── fail ├── map_outlives_closure.rs ├── map_outlives_closure.stderr ├── new_dep_outlives_closure.rs ├── new_dep_outlives_closure.stderr ├── new_dep_outlives_obj.rs └── new_dep_outlives_obj.stderr ├── game.md └── game.rs /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fortify" 3 | version = "0.4.1" 4 | edition = "2021" 5 | authors = ["Dmitry Zamkov "] 6 | description = "A simple and convenient way to bundle owned data with a borrowing type" 7 | repository = "https://github.com/dzamkov/fortify" 8 | license = "MIT OR Apache-2.0" 9 | keywords = ["reference", "lifetime", "ownership"] 10 | categories = ["memory-management", "rust-patterns"] 11 | 12 | [workspace] 13 | members = ["derive"] 14 | 15 | [dependencies] 16 | fortify_derive = { path = "derive", version = "0.2.0" } 17 | 18 | [target.'cfg(not(miri))'.dev-dependencies] 19 | trybuild = "1.0" -------------------------------------------------------------------------------- /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 2022 Dmitry Zamkov 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 | MIT License 2 | 3 | Copyright (c) 2022 Dmitry Zamkov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | A simple and convenient way to bundle owned data with a borrowing type. 2 | 3 | ## Example 4 | ```rust 5 | use fortify::*; 6 | 7 | // Define a borrowing type. The `Lower` trait specifies that it is covariant in its first 8 | // lifetime parameter. 9 | #[derive(Lower)] 10 | struct Example<'a> { 11 | a: &'a i32, 12 | b: &'a mut i32, 13 | } 14 | 15 | // Construct a fortified value that makes an "external" reference to `a` and an "internal" 16 | // reference to `b`, which is owned by the Fortify. 17 | let a = 1; 18 | let mut example: Fortify = fortify! { 19 | let mut b = 1; 20 | b += 1; 21 | yield Example { 22 | a: &a, 23 | b: &mut b 24 | }; 25 | }; 26 | 27 | // Use `with_mut` for general mutable access to the wrapped value. Note that the reference 28 | // to `b` is still valid even though `b` is not in scope in this block. 29 | example.with_mut(|example| { 30 | assert_eq!(*example.a, 1); 31 | assert_eq!(*example.b, 2); 32 | *example.b += 1; 33 | assert_eq!(*example.b, 3); 34 | }); 35 | ``` 36 | 37 | ## The Problem 38 | 39 | One of the main selling points of Rust is its borrow checker, which allows you to define types 40 | that make references to outside data while ensuring that the data will remain valid for as long 41 | the type is using it. In theory, this is the ideal memory management scheme: it costs nothing to 42 | "drop" a reference, we're not putting off any work for a deferred garbage collection and we're 43 | completely protected from use after free errors. 44 | 45 | However, many Rust programmers feel uneasy putting this technique into practice, often resorting 46 | to simpler methods such as reference counting. A key reason for this is the infectious nature of 47 | lifetimes: storing a reference in a type requires you to specify a lifetime, and with the rare 48 | exception of `'static` data, this requires you to have a lifetime parameter on the type. Then, to 49 | use the type in another type, you need to specify a lifetime parameter for that type, and so on. 50 | The chain only ends when you finally introduce the value as a local variable, allowing new unique 51 | lifetimes to be created for it. 52 | 53 | But this isn't always possible. Sometimes you need a value to be allocated at a higher stack level 54 | than you have access to. It's difficult to identify these situations ahead of time, 55 | and when you do, your whole design collapses. No wonder Rust programmers are hesitant about using 56 | references. 57 | 58 | There's a few existing solutions to mitigate this problem, but all of them have their issues: 59 | * Using an arena allocator such as [bumpalo](https://crates.io/crates/bumpalo) or 60 | [typed-arena](https://crates.io/crates/typed-arena), you can allocate data with a particular 61 | lifetime from a lower level in the stack. However, the memory can't be freed until the allocator 62 | is dropped, so there is a practical limit to how long the allocator can be kept alive. 63 | * The [owning_ref](https://crates.io/crates/owning_ref) crate allows you to avoid specifying the 64 | lifetime for a reference by bundling it with the data it is referencing. However, it has 65 | [numerous](https://github.com/Kimundi/owning-ref-rs/issues/49) 66 | [soundness](https://github.com/Kimundi/owning-ref-rs/issues/61) 67 | [issues](https://github.com/Kimundi/owning-ref-rs/issues/77) and is no longer being maintained. 68 | * There have been proposals for allowing self-referential structs. In lieu of language support, 69 | the [rental](https://crates.io/crates/rental) and [ouroboros](https://crates.io/crates/ouroboros) 70 | crates enable a limited form of this. However, the implementation of self-referential structs is 71 | not as simple or intuitive as one would expect. There are limitations to what may be stored in 72 | the struct and access to struct fields must be restricted in order to adhere to Rust's aliasing 73 | rules. 74 | 75 | This crate introduces yet another solution to problem, with the goal of being flexible and 76 | convenient enough to enable fearless use of references and borrowing types. 77 | 78 | ## The Solution 79 | 80 | The `Fortify` wrapper allows a wrapped value to make references to hidden, supplementary 81 | data owned by the wrapper itself. For example, a `Fortify<&'static str>` can be a regular 82 | `&'static str`, or it can be a reference to a string stored inside the `Fortify`. `T` isn't limited 83 | to being a reference, it can be any type that has a lifetime parameter, with similar 84 | effect. The implication here is that you can turn any borrowing type (i.e. a type with a lifetime 85 | parameter) into an owning type by setting its lifetime to `'static` and putting it in a `Fortify` 86 | wrapper. 87 | 88 | **How is this okay? Doesn't a `&'static str` always have to reference something in the `'static` 89 | lifetime?** 90 | 91 | The key insight is that you can never get a `&'static str` from a `Fortify<&'static str>`. Instead, 92 | you can get a `&'a str` where `'a` is tied to lifetime of the `Fortify`. The wrapper has a complex 93 | relationship with its wrapped type that can't normally be expressed in Rust (hence the need for 94 | this crate). It's implementation requires being able to manipulate the lifetime parameter of 95 | the enclosed type. 96 | 97 | **So if I use a type with multiple lifetime parameters, how does the wrapper know which lifetime 98 | it "works" on?** 99 | 100 | All wrapped types must implement the `Lower<'a>` trait, which specifies how to substitute the 101 | covariant lifetime parameters in the type. This trait can be automatically derived, in which 102 | case it will only operate on the first lifetime parameter in the parameter list. 103 | 104 | **How do I create a `Fortify`?** 105 | 106 | There are many ways to create an instance of the wrapper, with the simplest being a direct 107 | conversion from `T`. However, the preferred and most general method is the `fortify!` macro: 108 | 109 | ```rust 110 | let example: Fortify<&'static str> = fortify! { 111 | let mut str = String::new(); 112 | str.push_str("Foo"); 113 | str.push_str("Bar"); 114 | yield str.as_str(); 115 | }; 116 | ``` 117 | 118 | This captures all of the local variables in a block of code and stores them inside the `Fortify` 119 | wrapper. The final `yield` statement provides the wrapped value that is exposed to the outside. 120 | Note that it may make references to the local variables. 121 | 122 | **How do I use it?** 123 | 124 | You can use `borrow` to get an immutable reference to the wrapped value with appropriately 125 | shortened lifetime. Mutable access is a bit more complicated, and requires the 126 | use of `with_mut`. 127 | 128 | ```rust 129 | assert_eq!(example.borrow(), &"FooBar"); 130 | // or 131 | example.with_mut(|s| assert_eq!(s, &"FooBar")); 132 | ``` 133 | 134 | **Can I use `Fortify` with a non-`'static` lifetime?** 135 | 136 | Of course! The `Fortify` wrapper merely introduces an additional option for references (pointing to 137 | owned data inside the wrapper). It is always possible to forgo this option and construct a 138 | `Fortify` directly from a `T`. You can even have a mixed value which makes some references to 139 | external data and some references to owned data, as in the first example. -------------------------------------------------------------------------------- /derive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "fortify_derive" 3 | version = "0.2.0" 4 | edition = "2021" 5 | authors = ["Dmitry Zamkov "] 6 | description = "Derive macros for fortify" 7 | repository = "https://github.com/dzamkov/fortify" 8 | license = "MIT OR Apache-2.0" 9 | 10 | [lib] 11 | name = "fortify_derive" 12 | proc-macro = true 13 | 14 | [dependencies] 15 | proc-macro2 = "1.0" 16 | quote = "1.0" 17 | syn = { version = "1.0", features = ["full"] } -------------------------------------------------------------------------------- /derive/src/lib.rs: -------------------------------------------------------------------------------- 1 | use proc_macro::TokenStream; 2 | use quote::quote; 3 | use syn::*; 4 | 5 | #[proc_macro_derive(Lower)] 6 | pub fn derive_lower(input: TokenStream) -> TokenStream { 7 | let input = parse_macro_input!(input as DeriveInput); 8 | let name = input.ident; 9 | let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); 10 | let mut ex_generics: Generics = parse2(quote! { #impl_generics }).unwrap(); 11 | let mut alt_generics: Generics = parse2(quote! { #ty_generics }).unwrap(); 12 | let ex_lifetime: Lifetime = parse_quote! { '__lower }; 13 | ex_generics.params.insert(0, GenericParam::Lifetime(LifetimeDef::new(ex_lifetime.clone()))); 14 | 15 | // Substitute first lifetime parameter if it exists 16 | if let Some(GenericParam::Lifetime(def)) = &mut alt_generics.params.first_mut() { 17 | *def = LifetimeDef::new(ex_lifetime.clone()) 18 | } 19 | 20 | // Add lifetime bound to where clause 21 | let add_where_clause: WhereClause = parse_quote! { where #name #alt_generics: #ex_lifetime }; 22 | let where_clause = match where_clause { 23 | Some(cur_where_clause) => { 24 | let mut where_clause = cur_where_clause.clone(); 25 | for predicate in add_where_clause.predicates { 26 | where_clause.predicates.push(predicate); 27 | } 28 | where_clause 29 | } 30 | None => add_where_clause 31 | }; 32 | 33 | // Build implementation 34 | TokenStream::from(quote! { 35 | unsafe impl #ex_generics ::fortify::Lower<#ex_lifetime> for #name #ty_generics #where_clause { 36 | type Target = #name #alt_generics; 37 | fn lower_ref<'__ref>(&'__ref self) -> &'__ref Self::Target 38 | where 39 | Self: #ex_lifetime, 40 | #ex_lifetime: '__ref, 41 | { 42 | self 43 | } 44 | } 45 | }) 46 | } -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #![warn(missing_docs)] 2 | //! This crate provides the [`Fortify`] wrapper type. When used with a borrowing type (i.e. a type 3 | //! with a lifetime parameter) it allows values of that type to reference arbitrary data owned by 4 | //! the `Fortify` itself. 5 | //! 6 | //! # Example 7 | //! ``` 8 | //! use fortify::*; 9 | //! 10 | //! // Define a borrowing type. The `Lower` trait specifies that it is covariant in its first 11 | //! // lifetime parameter. 12 | //! #[derive(Lower)] 13 | //! struct Example<'a> { 14 | //! a: &'a i32, 15 | //! b: &'a mut i32, 16 | //! } 17 | //! 18 | //! // Construct a fortified value that makes an "external" reference to `a` and an "internal" 19 | //! // reference to `b`, which is owned by the Fortify. 20 | //! let a = 1; 21 | //! let mut example: Fortify = fortify! { 22 | //! let mut b = 1; 23 | //! b += 1; 24 | //! yield Example { 25 | //! a: &a, 26 | //! b: &mut b 27 | //! }; 28 | //! }; 29 | //! 30 | //! // Use `with_mut` for general mutable access to the wrapped value. Note that the reference 31 | //! // to `b` is still valid even though `b` is not in scope in this block. 32 | //! example.with_mut(|example| { 33 | //! assert_eq!(*example.a, 1); 34 | //! assert_eq!(*example.b, 2); 35 | //! *example.b += 1; 36 | //! assert_eq!(*example.b, 3); 37 | //! }); 38 | //! ``` 39 | extern crate self as fortify; 40 | mod lower; 41 | 42 | pub use fortify_derive::*; 43 | pub use lower::*; 44 | use std::future::Future; 45 | use std::mem::{transmute_copy, ManuallyDrop, MaybeUninit}; 46 | use std::pin::Pin; 47 | use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker}; 48 | 49 | /// Wraps a value of type `T` and allows it to reference arbitrary supplementary data owned by the 50 | /// [`Fortify`]. This can be used to effectively convert a borrowing type into an owning type. 51 | /// 52 | /// # Example 53 | /// ``` 54 | /// use fortify::*; 55 | /// let example: Fortify<&'static str> = fortify! { 56 | /// let mut str = String::new(); 57 | /// str.push_str("Foo"); 58 | /// str.push_str("Bar"); 59 | /// yield str.as_str(); 60 | /// }; 61 | /// example.with_ref(|s| assert_eq!(s, &"FooBar")); 62 | /// assert_eq!(example.borrow(), &"FooBar"); 63 | /// ``` 64 | pub struct Fortify { 65 | value: ManuallyDrop, 66 | data_raw: *mut (), 67 | data_drop_fn: unsafe fn(*mut ()), 68 | } 69 | 70 | impl Fortify { 71 | /// Directly constructs a [`Fortify`] wrapper over the given value. 72 | pub fn new(value: T) -> Self { 73 | Self { 74 | value: ManuallyDrop::new(value), 75 | data_raw: std::ptr::null_mut(), 76 | data_drop_fn: drop_nop, 77 | } 78 | } 79 | } 80 | 81 | impl<'a, T: Refers<'a>> Fortify { 82 | /// Creates a [`Fortify`] by explicitly providing its owned data and constructing its value 83 | /// from that using a closure. Note that for technical reasons, the constructed value must be 84 | /// wrapped in a [`Lowered`] wrapper. 85 | /// 86 | /// # Example 87 | /// ``` 88 | /// use fortify::{Fortify, Lowered}; 89 | /// let mut str = String::new(); 90 | /// str.push_str("Hello"); 91 | /// let fortified: Fortify<&str> = Fortify::new_dep(str, |s| Lowered::new(s.as_str())); 92 | /// assert_eq!(fortified.borrow(), &"Hello"); 93 | /// ``` 94 | pub fn new_dep(owned: O, cons: C) -> Self 95 | where 96 | C: for<'b> FnOnce(&'b mut O) -> Lowered<'b, T>, 97 | { 98 | Self::new_box_dep(Box::new(owned), cons) 99 | } 100 | 101 | /// Creates a [`Fortify`] by explicitly providing its owned data (as a [`Box`]) and 102 | /// constructing its value from that using a closure. Note that for technical reasons, the 103 | /// constructed value must be wrapped in a [`Lowered`] wrapper. 104 | pub fn new_box_dep(owned: Box, cons: C) -> Self 105 | where 106 | C: for<'b> FnOnce(&'b mut O) -> Lowered<'b, T>, 107 | { 108 | let owned = Box::into_raw(owned); 109 | let value = cons(unsafe { &mut *owned }); 110 | Self { 111 | value: ManuallyDrop::new(value.value), 112 | data_raw: owned as *mut (), 113 | data_drop_fn: drop_box_from_raw::, 114 | } 115 | } 116 | 117 | /// Creates a [`Fortify`] by using a [`Future`] to construct the `Fortify`'s value. As soon 118 | /// as the `Future` "yields" a value, it will be suspended and become the supplementary data 119 | /// for the `Fortify`. This allows the inner value to reference locals defined by the `Future`. 120 | /// 121 | /// The `Future` must await on [`FortifyYielder::yield_`] and nothing else. Code following the 122 | /// await may or may not be executed. 123 | /// 124 | /// This is a hacky way of taking advantage of rust's code generation for async in order to 125 | /// suspend an executing block of code. In the future, when 'generators' is stabilized, this 126 | /// would be unnecessary. Therefore, it is recommended to use the [`fortify!`] macro instead. 127 | /// 128 | /// # Example 129 | /// ``` 130 | /// use fortify::{Fortify, Lowered}; 131 | /// let external = 1; 132 | /// let mut fortified: Fortify<(&i32, &i32)> = Fortify::new_async(|y| async { 133 | /// let internal = 2; 134 | /// y.yield_(Lowered::new((&external, &internal))).await; 135 | /// }); 136 | /// let (external_ref, internal_ref) = *fortified.borrow(); 137 | /// assert_eq!(*external_ref, 1); 138 | /// assert_eq!(*internal_ref, 2); 139 | /// ``` 140 | pub fn new_async(cons: C) -> Self 141 | where 142 | C: 'a + FnOnce(FortifyYielder) -> F, 143 | F: 'a + Future, 144 | { 145 | let waker = nop_waker(); 146 | let mut cx = Context::from_waker(&waker); 147 | let mut data = FortifyYielderData { 148 | value: MaybeUninit::uninit(), 149 | tracker: FortifyYielderTracker { 150 | cx_ptr: &cx as *const Context as *const (), 151 | has_awaited: false, 152 | }, 153 | }; 154 | let future = Box::into_raw(Box::new(cons(FortifyYielder(&mut data)))); 155 | match Future::poll(unsafe { Pin::new_unchecked(&mut *future) }, &mut cx) { 156 | Poll::Ready(_) => { 157 | unsafe { drop_box_from_raw::(future as *mut ()) }; 158 | panic!("Future must await on FortifyYielder::yield_") 159 | } 160 | Poll::Pending => { 161 | if data.tracker.has_awaited { 162 | Self { 163 | value: ManuallyDrop::new(unsafe { 164 | transmute_copy(data.value.assume_init_ref()) 165 | }), 166 | data_raw: future as *mut (), 167 | data_drop_fn: drop_box_from_raw::, 168 | } 169 | } else { 170 | unsafe { drop_box_from_raw::(future as *mut ()) }; 171 | panic!("Future may only await on FortifyYielder::yield_") 172 | } 173 | } 174 | } 175 | } 176 | } 177 | 178 | impl<'a, T: Lower<'a, Target = T>> Fortify<&'a T> { 179 | /// Creates a [`Fortify`] by taking ownership of a [`Box`] and wrapping a reference to 180 | /// the value inside it. 181 | /// 182 | /// # Example 183 | /// ``` 184 | /// use fortify::Fortify; 185 | /// let value = Box::new(123); 186 | /// let mut fortified: Fortify<&i32> = Fortify::new_box_ref(value); 187 | /// assert_eq!(**fortified.borrow(), 123); 188 | /// assert_eq!(fortified.with_inner(|x| *x), 123); 189 | /// ``` 190 | pub fn new_box_ref(value: Box) -> Self { 191 | Self::new_box_dep(value, |inner| Lowered::new(&*inner)) 192 | } 193 | } 194 | 195 | impl<'a, T: 'a> Fortify<&'a mut T> { 196 | /// Creates a [`Fortify`] by taking ownership of a [`Box`] and wrapping a mutable reference to 197 | /// the value inside it. 198 | /// 199 | /// # Example 200 | /// ``` 201 | /// use fortify::Fortify; 202 | /// let value = Box::new(123); 203 | /// let mut fortified: Fortify<&mut i32> = Fortify::new_box_mut(value); 204 | /// fortified.with_mut(|v| **v *= 2); 205 | /// assert_eq!(**fortified.borrow(), 246); 206 | /// ``` 207 | pub fn new_box_mut(value: Box) -> Self { 208 | Self::new_box_dep(value, |inner| Lowered::new(inner)) 209 | } 210 | } 211 | 212 | impl<'a, T: Lower<'a>> Fortify { 213 | /// Immutably borrows the value inside a [`Fortify`]. For more general access to the wrapped 214 | /// value, see [`Fortify::with_ref`] and [`Fortify::with_mut`]. 215 | #[allow(clippy::should_implement_trait)] 216 | // We would like to implement `std::borrow::Borrow`, but it's not possible to specify the 217 | // lifetime correctly. 218 | pub fn borrow(&'a self) -> &'a >::Target { 219 | let value = &*self.value; 220 | unsafe { transmute_copy(&value) } 221 | } 222 | } 223 | 224 | impl Lower<'a>> Fortify { 225 | /// Executes a closure using an immutable reference to the value stored inside this [`Fortify`]. 226 | /// 227 | /// Calls to `with_ref` can typically be replaced with and simplified using `borrow`. This 228 | /// method is retained for consistency with `with_mut` and possible support for non-covariant 229 | /// types (which can't use `borrow`) in the future. 230 | pub fn with_ref<'a, F, R>(&'a self, f: F) -> R 231 | where 232 | F: for<'b> FnOnce(&'a >::Target) -> R, 233 | { 234 | let value = &*self.value; 235 | f(unsafe { transmute_copy(&value) }) 236 | } 237 | 238 | /// Executes a closure using a mutable reference to the value stored inside this [`Fortify`]. 239 | pub fn with_mut<'a, F, R>(&'a mut self, f: F) -> R 240 | where 241 | F: for<'b> FnOnce(&'a mut >::Target) -> R, 242 | { 243 | let value = &mut *self.value; 244 | f(unsafe { transmute_copy(&value) }) 245 | } 246 | 247 | /// Executes a closure with the value stored inside this [`Fortify`], effectively destructing 248 | /// the wrapper. 249 | pub fn with_inner(self, f: F) -> R 250 | where 251 | for<'a> >::Target: Sized, 252 | F: for<'a> FnOnce(>::Target) -> R, 253 | { 254 | self.split(|inner| (Lowered::new(()), f(Lowered::unwrap(inner)))) 255 | .1 256 | } 257 | } 258 | 259 | impl<'a, T: 'a> Fortify { 260 | /// Maps and splits this [`Fortify`] wrapper into a component that references its owned 261 | /// data, and a component that doesn't. This is a generalization of both [`Fortify::map`] and 262 | /// [`Fortify::with_inner`]. 263 | /// 264 | /// # Example 265 | /// ``` 266 | /// use fortify::*; 267 | /// let fortified: Fortify<(&i32, i32)> = fortify! { 268 | /// let x = 12; 269 | /// yield (&x, 15); 270 | /// }; 271 | /// let (x, y) = fortified.split(|inner| (Lowered::new(inner.0), inner.1)); 272 | /// assert_eq!(**x.borrow(), 12); 273 | /// assert_eq!(y, 15); 274 | /// ``` 275 | pub fn split(mut self, f: F) -> (Fortify, R) 276 | where 277 | N: Refers<'a>, 278 | F: for<'b> FnOnce(Lowered<'b, T>) -> (Lowered<'b, N>, R), 279 | { 280 | let value = unsafe { ManuallyDrop::take(&mut self.value) }; 281 | let data_raw = self.data_raw; 282 | let data_drop_fn = self.data_drop_fn; 283 | std::mem::forget(self); 284 | let (value, res) = f(Lowered { 285 | value, 286 | marker: std::marker::PhantomData, 287 | }); 288 | ( 289 | Fortify { 290 | value: ManuallyDrop::new(value.value), 291 | data_raw, 292 | data_drop_fn, 293 | }, 294 | res, 295 | ) 296 | } 297 | 298 | /// Constructs a new [`Fortify`] wrapper by applying a mapping function to the value stored 299 | /// in this wrapper. The resulting [`Fortify`] will carry the exact same owned data as this 300 | /// does. 301 | pub fn map(self, f: F) -> Fortify 302 | where 303 | N: Refers<'a>, 304 | F: for<'b> FnOnce(Lowered<'b, T>) -> Lowered<'b, N>, 305 | { 306 | self.split(|inner| (f(inner), ())).0 307 | } 308 | } 309 | 310 | /// Indicates that, if this type has a non-trivial implementation of [`Lower`], it references 311 | /// the lifetime `'a`. Thus, the bound `T: Refers<'a>` can be thought of as the inverse of `T: 'a`. 312 | /// 313 | /// This is used by various [`Fortify`]-constructing functions to ensure that the resulting 314 | /// wrapper does not outlive the external references it contains. This will be automatically 315 | /// implemented for any type that correctly implements [`Lower`]. 316 | pub trait Refers<'a> {} 317 | 318 | impl<'a, T: Lower<'a, Target = T>> Refers<'a> for T {} 319 | 320 | impl From for Fortify { 321 | fn from(value: T) -> Self { 322 | Fortify::new(value) 323 | } 324 | } 325 | 326 | impl<'a, T: Lower<'a, Target = T>> From> for Fortify<&'a T> { 327 | fn from(value: Box) -> Self { 328 | Fortify::new_box_ref(value) 329 | } 330 | } 331 | 332 | impl<'a, T> From> for Fortify<&'a mut T> { 333 | fn from(value: Box) -> Self { 334 | Fortify::new_box_mut(value) 335 | } 336 | } 337 | 338 | impl Default for Fortify { 339 | fn default() -> Self { 340 | Fortify::new(T::default()) 341 | } 342 | } 343 | 344 | impl std::fmt::Debug for Fortify 345 | where 346 | for<'a> T: Lower<'a>, 347 | for<'a> >::Target: std::fmt::Debug, 348 | { 349 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 350 | self.borrow().fmt(f) 351 | } 352 | } 353 | 354 | impl std::fmt::Display for Fortify 355 | where 356 | for<'a> T: Lower<'a>, 357 | for<'a> >::Target: std::fmt::Display, 358 | { 359 | fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { 360 | self.borrow().fmt(f) 361 | } 362 | } 363 | 364 | unsafe impl<'a, T: Lower<'a>> Lower<'a> for Fortify 365 | where 366 | T::Target: Sized, 367 | { 368 | type Target = Fortify; 369 | } 370 | 371 | impl Iterator for Fortify 372 | where 373 | for<'a> T: Lower<'a>, 374 | for<'a> >::Target: Iterator, 375 | { 376 | type Item = T::Item; 377 | fn next(&mut self) -> Option { 378 | self.with_mut(|inner| inner.next()) 379 | } 380 | } 381 | 382 | impl Drop for Fortify { 383 | fn drop(&mut self) { 384 | unsafe { 385 | // Value must be dropped before data 386 | ManuallyDrop::drop(&mut self.value); 387 | (self.data_drop_fn)(self.data_raw); 388 | } 389 | } 390 | } 391 | 392 | /// Does nothing. 393 | unsafe fn drop_nop(_: *mut ()) { 394 | // Nothing to do here 395 | } 396 | 397 | /// Constructs a box from its raw pointer and then drops it. 398 | unsafe fn drop_box_from_raw(raw: *mut ()) { 399 | // NOTE: It may seem easier to convert to a box and drop it here, but that may trigger UB if 400 | // the box contains self-references (which is common with futures). Instead, we'll use the 401 | // destruction pattern from https://doc.rust-lang.org/std/boxed/struct.Box.html#method.into_raw 402 | std::ptr::drop_in_place(raw as *mut T); 403 | let layout = std::alloc::Layout::new::(); 404 | if layout.size() > 0 { 405 | std::alloc::dealloc(raw as *mut u8, layout); 406 | } 407 | } 408 | 409 | /// A [`Waker`] that does nothing when waked. 410 | fn nop_waker() -> Waker { 411 | const VTABLE: &RawWakerVTable = &RawWakerVTable::new(clone, nop, nop, nop); 412 | unsafe fn clone(data: *const ()) -> RawWaker { 413 | RawWaker::new(data, VTABLE) 414 | } 415 | unsafe fn nop(_: *const ()) {} 416 | unsafe { Waker::from_raw(RawWaker::new(std::ptr::null(), VTABLE)) } 417 | } 418 | 419 | /// A helper interface used by the [`Fortify::new_async`] constructor. 420 | pub struct FortifyYielder(*mut FortifyYielderData); 421 | 422 | impl FortifyYielder { 423 | /// Provides the [`Fortify`] value to this [`FortifyYielder`] and returns a [`Future`] that may 424 | /// be awaited to suspend execution. 425 | pub fn yield_(self, value: Lowered) -> impl Future + '_ { 426 | unsafe { 427 | let target = &mut *self.0; 428 | target.value.write(value.value); 429 | FortifyYielderFuture(&mut target.tracker) 430 | } 431 | } 432 | } 433 | 434 | struct FortifyYielderData { 435 | value: MaybeUninit, 436 | tracker: FortifyYielderTracker, 437 | } 438 | 439 | struct FortifyYielderTracker { 440 | cx_ptr: *const (), 441 | has_awaited: bool, 442 | } 443 | 444 | struct FortifyYielderFuture(*mut FortifyYielderTracker); 445 | 446 | impl Future for FortifyYielderFuture { 447 | type Output = (); 448 | 449 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 450 | unsafe { 451 | // Verify the context to ensure that this future is being polled by `new_async` rather 452 | // than by the user. 453 | let tracker = &mut *self.as_ref().0; 454 | if tracker.cx_ptr == (cx as *const Context as *const ()) { 455 | // Inform `new_async` that the future has been awaited. This enables the value 456 | // written to `FortifyYielderData` to be used. 457 | tracker.has_awaited = true; 458 | } 459 | } 460 | Poll::Pending 461 | } 462 | } 463 | 464 | /// A helper macro for creating a `Fortify` using generator-like syntax. The macro takes a block of 465 | /// statements that ends with a `yield` of some expression. The block will be executed up to the 466 | /// `yield` statement, at which point the value of expression will be bundled with the suspended 467 | /// scope of the block and returned as a `Fortify`ied value. Local variables defined in the block 468 | /// may be accessed through references in the wrapped value. 469 | /// 470 | /// # Example 471 | /// ``` 472 | /// use fortify::*; 473 | /// let external = 1; 474 | /// let mut fortified: Fortify<(&i32, &i32)> = fortify! { 475 | /// let internal = 2; 476 | /// yield (&external, &internal); 477 | /// }; 478 | /// let (external_ref, internal_ref) = *fortified.borrow(); 479 | /// assert_eq!(*external_ref, 1); 480 | /// assert_eq!(*internal_ref, 2); 481 | /// ``` 482 | #[macro_export] 483 | macro_rules! fortify { 484 | (@INNER $y:ident , yield $res:expr ;) => { 485 | $y.yield_(Lowered::new($res)).await 486 | }; 487 | (@INNER $y:ident , $st:stmt ; $($t:tt)*) => { 488 | { $st fortify!(@INNER $y , $($t)*) } 489 | }; 490 | ($($t:tt)*) => { 491 | $crate::Fortify::new_async(move |y| async move { 492 | fortify!(@INNER y , $($t)*) 493 | }) 494 | }; 495 | } 496 | 497 | #[cfg(test)] 498 | mod tests; 499 | -------------------------------------------------------------------------------- /src/lower.rs: -------------------------------------------------------------------------------- 1 | use std::marker::PhantomData; 2 | use std::mem::{transmute_copy, ManuallyDrop}; 3 | 4 | /// Indicates that a type [covariantly](https://doc.rust-lang.org/nomicon/subtyping.html) 5 | /// references a set of lifetime parameters, and when these parameters are all replaced with `'a`, 6 | /// the resulting type is `Target`. Consequentially, if the type outlives `'a`, it can be directly 7 | /// coerced to `Target` by applying covariance. 8 | /// 9 | /// This trait can be trivially implemented for any type by setting `Target` to be `Self`. However, 10 | /// in order to maximize its usefulness, it should operate on as many lifetime parameters as 11 | /// possible. 12 | /// 13 | /// This trait can be automatically derived. When deriving on a type with no lifetime parameters, 14 | /// the trivial implementation will be used (i.e. `Target = Self`). Otherwise, it will operate 15 | /// on the first lifetime parameter in the generic parameters list. Deriving will fail if the 16 | /// type is not covariant in this parameter. 17 | /// 18 | /// # Safety 19 | /// 20 | /// The implementor is responsible for ensuring that for all `'a` where `T: 'a`, 21 | /// `>::Target` is a subtype of `T`. 22 | pub unsafe trait Lower<'a> { 23 | /// The type resulting from substituting the covariant lifetime parameters in `Self` for `'a`. 24 | type Target: Lower<'a, Target = Self::Target> + ?Sized + 'a; 25 | 26 | /// Applies convariance in order to shorten the internal lifetime parameters in a reference. 27 | fn lower_ref<'b>(&'b self) -> &'b Self::Target 28 | where 29 | Self: 'a, 30 | 'a: 'b, 31 | { 32 | // SAFETY: The implementor of the trait is responsible for ensuring that `Target` is 33 | // a covariant specialization of `Self`. Assuming this, we can directly coerce `Self` to 34 | // `Target`. 35 | unsafe { transmute_copy(&self) } 36 | } 37 | } 38 | 39 | unsafe impl<'a, 'b, T: Lower<'b> + ?Sized> Lower<'b> for &'a T { 40 | type Target = &'b >::Target; 41 | } 42 | 43 | unsafe impl<'a, 'b, T: 'b + ?Sized> Lower<'b> for &'a mut T { 44 | type Target = &'b mut T; 45 | fn lower_ref<'c>(&'c self) -> &'c Self::Target 46 | where 47 | 'a: 'b, 48 | 'b: 'c, 49 | { 50 | self 51 | } 52 | } 53 | 54 | unsafe impl<'a, T: Lower<'a> + ?Sized> Lower<'a> for Box { 55 | type Target = Box<>::Target>; 56 | } 57 | 58 | unsafe impl<'a, T: Lower<'a>> Lower<'a> for [T] 59 | where 60 | >::Target: Sized, 61 | { 62 | type Target = [>::Target]; 63 | } 64 | 65 | unsafe impl<'a, T: Lower<'a>, const N: usize> Lower<'a> for [T; N] 66 | where 67 | >::Target: Sized, 68 | { 69 | type Target = [>::Target; N]; 70 | } 71 | 72 | unsafe impl<'a, A: Lower<'a>, B: Lower<'a> + ?Sized> Lower<'a> for (A, B) 73 | where 74 | >::Target: Sized, 75 | { 76 | type Target = (>::Target, >::Target); 77 | } 78 | 79 | unsafe impl<'a, A: Lower<'a>, B: Lower<'a>, C: Lower<'a> + ?Sized> Lower<'a> for (A, B, C) 80 | where 81 | >::Target: Sized, 82 | >::Target: Sized, 83 | { 84 | type Target = ( 85 | >::Target, 86 | >::Target, 87 | >::Target, 88 | ); 89 | } 90 | 91 | unsafe impl<'a, A: Lower<'a>, B: Lower<'a>, C: Lower<'a>, D: Lower<'a> + ?Sized> Lower<'a> 92 | for (A, B, C, D) 93 | where 94 | >::Target: Sized, 95 | >::Target: Sized, 96 | >::Target: Sized, 97 | { 98 | type Target = ( 99 | >::Target, 100 | >::Target, 101 | >::Target, 102 | >::Target, 103 | ); 104 | } 105 | 106 | unsafe impl<'a, 'b, T: Lower<'b>> Lower<'b> for std::slice::Iter<'a, T> 107 | where 108 | >::Target: Sized, 109 | { 110 | type Target = std::slice::Iter<'b, >::Target>; 111 | } 112 | 113 | unsafe impl<'a, I: Lower<'a>> Lower<'a> for std::iter::Copied 114 | where 115 | >::Target: Sized, 116 | { 117 | type Target = std::iter::Copied<>::Target>; 118 | } 119 | 120 | macro_rules! impl_trivial_lower { 121 | ($t:ty) => { 122 | unsafe impl<'a> Lower<'a> for $t { 123 | type Target = $t; 124 | fn lower_ref<'b>(&'b self) -> &'b Self::Target 125 | where 126 | Self: 'a, 127 | 'a: 'b, 128 | { 129 | self 130 | } 131 | } 132 | }; 133 | } 134 | 135 | impl_trivial_lower!(()); 136 | impl_trivial_lower!(bool); 137 | impl_trivial_lower!(char); 138 | impl_trivial_lower!(f32); 139 | impl_trivial_lower!(f64); 140 | impl_trivial_lower!(i8); 141 | impl_trivial_lower!(i16); 142 | impl_trivial_lower!(i32); 143 | impl_trivial_lower!(i64); 144 | impl_trivial_lower!(i128); 145 | impl_trivial_lower!(isize); 146 | impl_trivial_lower!(u8); 147 | impl_trivial_lower!(u16); 148 | impl_trivial_lower!(u32); 149 | impl_trivial_lower!(u64); 150 | impl_trivial_lower!(u128); 151 | impl_trivial_lower!(usize); 152 | impl_trivial_lower!(std::num::NonZeroI8); 153 | impl_trivial_lower!(std::num::NonZeroI16); 154 | impl_trivial_lower!(std::num::NonZeroI32); 155 | impl_trivial_lower!(std::num::NonZeroI64); 156 | impl_trivial_lower!(std::num::NonZeroI128); 157 | impl_trivial_lower!(std::num::NonZeroIsize); 158 | impl_trivial_lower!(std::num::NonZeroU8); 159 | impl_trivial_lower!(std::num::NonZeroU16); 160 | impl_trivial_lower!(std::num::NonZeroU32); 161 | impl_trivial_lower!(std::num::NonZeroU64); 162 | impl_trivial_lower!(std::num::NonZeroU128); 163 | impl_trivial_lower!(std::num::NonZeroUsize); 164 | impl_trivial_lower!(std::sync::atomic::AtomicI8); 165 | impl_trivial_lower!(std::sync::atomic::AtomicI16); 166 | impl_trivial_lower!(std::sync::atomic::AtomicI32); 167 | impl_trivial_lower!(std::sync::atomic::AtomicI64); 168 | impl_trivial_lower!(std::sync::atomic::AtomicIsize); 169 | impl_trivial_lower!(std::sync::atomic::AtomicU8); 170 | impl_trivial_lower!(std::sync::atomic::AtomicU16); 171 | impl_trivial_lower!(std::sync::atomic::AtomicU32); 172 | impl_trivial_lower!(std::sync::atomic::AtomicU64); 173 | impl_trivial_lower!(std::sync::atomic::AtomicUsize); 174 | impl_trivial_lower!(str); 175 | impl_trivial_lower!(String); 176 | impl_trivial_lower!(std::ffi::CStr); 177 | impl_trivial_lower!(std::ffi::CString); 178 | impl_trivial_lower!(std::ffi::OsStr); 179 | impl_trivial_lower!(std::ffi::OsString); 180 | 181 | /// A value of `T` with its lifetime shortened to `'a`. This is isomorphic to 182 | /// `>::Target`, but provides additional information to the compiler to assist 183 | /// type inferencing. 184 | pub struct Lowered<'a, T: 'a> { 185 | pub(crate) value: T, 186 | pub(crate) marker: PhantomData<&'a ()>, 187 | } 188 | 189 | impl<'a, T: 'a> Lowered<'a, T> { 190 | /// Constructs a [`Lowered`] from its wrapped value. 191 | /// 192 | /// The type signature of this function may look a bit odd, but it was carefully crafted 193 | /// to assist type inferencing and minimize spurious compiler errors. You can think of 194 | /// this function as taking a `>::Target`. 195 | pub fn new<'b, O>(value: O) -> Self 196 | where 197 | 'b: 'a, 198 | O: Lower<'b, Target = T> + 'a, 199 | { 200 | let value = unsafe { transmute_copy(&*ManuallyDrop::new(value)) }; 201 | Lowered { 202 | value, 203 | marker: PhantomData, 204 | } 205 | } 206 | 207 | /// Constructs a [`Lowered`] from its wrapped value. 208 | pub fn new_direct(value: >::Target) -> Self 209 | where 210 | T: Lower<'a>, 211 | >::Target: Sized, 212 | { 213 | let value = unsafe { transmute_copy(&*ManuallyDrop::new(value)) }; 214 | Lowered { 215 | value, 216 | marker: PhantomData, 217 | } 218 | } 219 | 220 | /// Unpacks this [`Lowered`] wrapper. 221 | pub fn unwrap(lowered: Self) -> >::Target 222 | where 223 | T: Lower<'a>, 224 | >::Target: Sized, 225 | { 226 | unsafe { transmute_copy(&*ManuallyDrop::new(lowered.value)) } 227 | } 228 | } 229 | 230 | impl<'a, T: Lower<'a>> std::ops::Deref for Lowered<'a, T> { 231 | type Target = >::Target; 232 | fn deref(&self) -> &Self::Target { 233 | // Although we could use `lower_ref` to safely implement this, that would not be 234 | // technically correct. `value` is logically a `>::Target`, not a `T`, so 235 | // it doesn't make sense to lower it. 236 | unsafe { transmute_copy(&&self.value) } 237 | } 238 | } 239 | 240 | impl<'a, T: Lower<'a>> std::ops::DerefMut for Lowered<'a, T> { 241 | fn deref_mut(&mut self) -> &mut Self::Target { 242 | unsafe { transmute_copy(&&mut self.value) } 243 | } 244 | } 245 | 246 | unsafe impl<'a, 'b, T: 'a + 'b> Lower<'b> for Lowered<'a, T> { 247 | type Target = Lowered<'b, T>; 248 | fn lower_ref<'c>(&'c self) -> &'c Self::Target 249 | where 250 | 'a: 'b, 251 | 'b: 'c, 252 | { 253 | self 254 | } 255 | } -------------------------------------------------------------------------------- /src/tests.rs: -------------------------------------------------------------------------------- 1 | use crate::*; 2 | use std::cell::Cell; 3 | 4 | #[test] 5 | fn test_complex() { 6 | let counter = &Cell::new(0); 7 | let fortified = fortify! { 8 | let a = Box::new([1, 1, 2, 3, 5, 8]); 9 | let b = 100; 10 | let c = fortify! { 11 | let d = 10000; 12 | let e = 500; 13 | let f = DropChecker::new(counter); 14 | yield (&b, &d, [&b, &d, &e], &f); 15 | }; 16 | let d = DropChecker::new(counter); 17 | yield (&a, Box::new(&a[1..3]), &c, d); 18 | }; 19 | fortified.with_ref(|inner| { 20 | assert_eq!(&**inner.0, &[1, 1, 2, 3, 5, 8]); 21 | assert_eq!(&*inner.1, &[1, 2]); 22 | inner.2.with_ref(|inner| { 23 | assert_eq!(*inner.0, 100); 24 | assert_eq!(*inner.1, 10000); 25 | assert_eq!(*inner.2[0], 100); 26 | assert_eq!(*inner.2[1], 10000); 27 | assert_eq!(*inner.2[2], 500); 28 | }); 29 | }); 30 | drop(fortified); 31 | assert_eq!(counter.get(), 0); 32 | } 33 | 34 | #[test] 35 | fn test_zst() { 36 | let _: Fortify<&'static ()> = Fortify::new_dep((), |r| Lowered::new(&*r)); 37 | } 38 | 39 | #[test] 40 | fn test_no_lifetime() { 41 | #[allow(dead_code)] 42 | #[derive(Lower)] 43 | struct Test 44 | where 45 | T: std::fmt::Display, 46 | { 47 | a: T, 48 | b: bool, 49 | } 50 | let fortified = fortify! { 51 | yield Test { a: 'x', b: true }; 52 | }; 53 | assert_eq!(fortified.borrow().a, 'x'); 54 | assert_eq!(fortified.borrow().b, true); 55 | } 56 | 57 | #[test] 58 | fn test_move() { 59 | // See https://github.com/dzamkov/fortify/issues/1 60 | #[derive(Lower)] 61 | pub struct I32<'a> { 62 | foo: &'a i32, 63 | } 64 | fn create(foo: i32) -> Fortify> { 65 | fortify! { 66 | let foo = foo; 67 | yield I32 { foo: &foo }; 68 | } 69 | } 70 | assert_eq!(*create(15).borrow().foo, 15); 71 | } 72 | 73 | #[test] 74 | fn test_debug() { 75 | #[allow(dead_code)] 76 | #[derive(Lower, Debug)] 77 | struct Test<'a> { 78 | a: &'a i32, 79 | b: &'a str, 80 | } 81 | let fortified = fortify! { 82 | let a = 13; 83 | let b = "Foo"; 84 | yield Test { a: &a, b: &b }; 85 | }; 86 | assert_eq!(format!("{:?}", fortified), "Test { a: 13, b: \"Foo\" }"); 87 | } 88 | 89 | #[test] 90 | fn test_iter() { 91 | let iter = Fortify::new_dep([1, 2, 3], |slice| Lowered::new(slice.iter().copied())); 92 | let mut sum = 0; 93 | for x in iter { 94 | sum += x; 95 | } 96 | assert_eq!(sum, 6); 97 | } 98 | 99 | /// A helper for testing that a value is dropped. 100 | #[derive(Lower)] 101 | struct DropChecker<'a>(&'a Cell); 102 | 103 | impl<'a> DropChecker<'a> { 104 | fn new(counter: &'a Cell) -> Self { 105 | counter.set(counter.get() + 1); 106 | Self(counter) 107 | } 108 | } 109 | 110 | impl<'a> Drop for DropChecker<'a> { 111 | fn drop(&mut self) { 112 | let old_count = self.0.get(); 113 | assert!(old_count > 0); 114 | self.0.set(old_count - 1); 115 | } 116 | } 117 | 118 | #[test] 119 | fn test_drop_new() { 120 | let counter = Cell::new(0); 121 | drop(Fortify::new(DropChecker::new(&counter))); 122 | assert_eq!(counter.get(), 0); 123 | } 124 | 125 | #[test] 126 | fn test_drop_new_box_ref() { 127 | let counter = Cell::new(0); 128 | drop(Fortify::new_box_ref(Box::new(DropChecker::new(&counter)))); 129 | assert_eq!(counter.get(), 0); 130 | } 131 | 132 | #[test] 133 | fn test_drop_new_box_mut() { 134 | let counter = Cell::new(0); 135 | drop(Fortify::new_box_mut(Box::new(DropChecker::new(&counter)))); 136 | assert_eq!(counter.get(), 0); 137 | } 138 | 139 | #[test] 140 | #[should_panic] 141 | #[allow(unused_must_use)] 142 | fn test_new_async_no_await() { 143 | let _ = Fortify::new_async(|y| async { 144 | let x = 42; 145 | y.yield_(Lowered::new(&x)); 146 | }); 147 | } 148 | 149 | struct NopFuture; 150 | impl Future for NopFuture { 151 | type Output = (); 152 | 153 | fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll { 154 | Poll::Pending 155 | } 156 | } 157 | 158 | #[test] 159 | #[should_panic] 160 | #[allow(unused_must_use)] 161 | fn test_new_async_bad_await() { 162 | let _ = Fortify::new_async(|y| async { 163 | let x = 42; 164 | let future = y.yield_(Lowered::new(&x)); 165 | 166 | // Try tricking `new_async` into thinking the future was awaited by polling on it 167 | let waker = nop_waker(); 168 | let mut fake_cx = Context::from_waker(&waker); 169 | Box::pin(future).as_mut().poll(&mut fake_cx); 170 | 171 | // .. but await a different future instead 172 | NopFuture.await; 173 | }); 174 | } 175 | 176 | #[test] 177 | #[cfg(not(miri))] 178 | fn test_fail() { 179 | let t = trybuild::TestCases::new(); 180 | t.compile_fail("tests/fail/*.rs"); 181 | } -------------------------------------------------------------------------------- /tests/config.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | use fortify::*; 3 | use std::collections::HashMap; 4 | 5 | pub struct User { 6 | pub name: String, 7 | pub data: u32, 8 | // ... 9 | } 10 | 11 | pub struct Config(Fortify>); 12 | 13 | #[derive(Lower)] 14 | struct ConfigInner<'a> { 15 | pub users: &'a Vec, 16 | pub map: HashMap<&'a str, &'a User>, 17 | } 18 | 19 | impl Config { 20 | pub fn new(users: Vec) -> Self { 21 | fn build_map<'a>(users: &'a Vec) -> HashMap<&'a str, &'a User> { 22 | let mut map = HashMap::new(); 23 | for user in users.iter() { 24 | map.insert(user.name.as_str(), user); 25 | } 26 | map 27 | } 28 | Config(fortify! { 29 | let users = users; 30 | yield ConfigInner { 31 | users: &users, 32 | map: build_map(&users) 33 | }; 34 | }) 35 | } 36 | 37 | pub fn get_user_by_name(&self, name: &str) -> Option<&User> { 38 | self.0.borrow().map.get(name).copied() 39 | } 40 | } 41 | 42 | #[test] 43 | fn test() { 44 | let config = Config::new(vec![ 45 | User { 46 | name: "Alice".to_string(), 47 | data: 24, 48 | }, 49 | User { 50 | name: "Bob".to_string(), 51 | data: 30, 52 | }, 53 | ]); 54 | assert!(config.get_user_by_name("Alice").unwrap().data == 24); 55 | assert!(config.get_user_by_name("Bob").unwrap().data == 30); 56 | assert!(config.get_user_by_name("Charlie").is_none()); 57 | } 58 | -------------------------------------------------------------------------------- /tests/fail/map_outlives_closure.rs: -------------------------------------------------------------------------------- 1 | use fortify::*; 2 | 3 | fn main() { 4 | let fortified = Fortify::new(456); 5 | let x = Box::new(123); 6 | let fortified = fortified.map(|_| Lowered::new(&*x)); 7 | drop(x); 8 | assert_eq!(**fortified.borrow(), 123); 9 | } -------------------------------------------------------------------------------- /tests/fail/map_outlives_closure.stderr: -------------------------------------------------------------------------------- 1 | error[E0505]: cannot move out of `x` because it is borrowed 2 | --> tests/fail/map_outlives_closure.rs:7:10 3 | | 4 | 6 | let fortified = fortified.map(|_| Lowered::new(&*x)); 5 | | --- -- borrow occurs due to use in closure 6 | | | 7 | | borrow of `*x` occurs here 8 | 7 | drop(x); 9 | | ^ move out of `x` occurs here 10 | 8 | assert_eq!(**fortified.borrow(), 123); 11 | | ------------------ borrow later used here 12 | -------------------------------------------------------------------------------- /tests/fail/new_dep_outlives_closure.rs: -------------------------------------------------------------------------------- 1 | use fortify::*; 2 | 3 | fn main() { 4 | let x = Box::new(123); 5 | let obj = 456; 6 | let fortified = Fortify::new_dep(obj, |_| Lowered::new(&*x)); 7 | drop(x); 8 | assert_eq!(**fortified.borrow(), 123); 9 | } -------------------------------------------------------------------------------- /tests/fail/new_dep_outlives_closure.stderr: -------------------------------------------------------------------------------- 1 | error[E0505]: cannot move out of `x` because it is borrowed 2 | --> tests/fail/new_dep_outlives_closure.rs:7:10 3 | | 4 | 6 | let fortified = Fortify::new_dep(obj, |_| Lowered::new(&*x)); 5 | | --- -- borrow occurs due to use in closure 6 | | | 7 | | borrow of `*x` occurs here 8 | 7 | drop(x); 9 | | ^ move out of `x` occurs here 10 | 8 | assert_eq!(**fortified.borrow(), 123); 11 | | ------------------ borrow later used here 12 | -------------------------------------------------------------------------------- /tests/fail/new_dep_outlives_obj.rs: -------------------------------------------------------------------------------- 1 | use fortify::*; 2 | 3 | fn main() { 4 | let x = Box::new(123); 5 | let obj = &*x; 6 | let fortified = Fortify::new_dep(obj, |val| Lowered::new(*val)); 7 | drop(x); 8 | assert_eq!(**fortified.borrow(), 123); 9 | } -------------------------------------------------------------------------------- /tests/fail/new_dep_outlives_obj.stderr: -------------------------------------------------------------------------------- 1 | error[E0505]: cannot move out of `x` because it is borrowed 2 | --> tests/fail/new_dep_outlives_obj.rs:7:10 3 | | 4 | 5 | let obj = &*x; 5 | | --- borrow of `*x` occurs here 6 | 6 | let fortified = Fortify::new_dep(obj, |val| Lowered::new(*val)); 7 | 7 | drop(x); 8 | | ^ move out of `x` occurs here 9 | 8 | assert_eq!(**fortified.borrow(), 123); 10 | | ------------------ borrow later used here 11 | -------------------------------------------------------------------------------- /tests/game.md: -------------------------------------------------------------------------------- 1 | Say we want to make a simple 2D game. The (imaginary) framework we're using provides the 2 | following API: 3 | 4 | ```rust 5 | /// An interactive application drawn on a two-dimensional surface. 6 | pub trait Application { 7 | /// Draws the UI for the application. 8 | fn draw(&self); 9 | 10 | /// Updates the state of the application in response to the passage of time. 11 | fn update(&mut self, step: f32); 12 | } 13 | 14 | /// Runs an [`Application`]. 15 | pub fn run_app(mut app: impl Application + 'static); 16 | 17 | /// An interface to a graphics device used for drawing and managing textures. 18 | pub struct Device { ... }; 19 | 20 | impl Device { 21 | /// Creates a new [`Device`]. 22 | pub fn new() -> Self; 23 | 24 | /// Loads a texture by name. 25 | pub fn load_texture(&self, name: &'static str) -> u32; 26 | 27 | /// Unloads a texture. 28 | pub fn unload_texture(&self, id: u32); 29 | 30 | /// Draws a rectangular section of a [`Texture`] to the screen. 31 | pub fn draw_texture<'a>( 32 | &self, 33 | id: u32, 34 | uv_min: (u32, u32), 35 | uv_max: (u32, u32), 36 | pos: (f32, f32), 37 | ); 38 | } 39 | ``` 40 | 41 | We will need to create an implementation of `Application` which manages the state for the game. 42 | This state includes a set of entities where each entity has a position, velocity and sprite used 43 | for drawing. A sprite is a rectangular section of a texture (sprites are packed together in the 44 | same texture for [performance reasons](https://en.wikipedia.org/wiki/Texture_atlas)). 45 | 46 | Feeling adventurous, we will model this with liberal use of references: 47 | 48 | ```rust 49 | /// Identifies a texture resource owned by a [`Device`]. 50 | pub struct Texture<'a> { 51 | device: &'a Device, 52 | id: u32 53 | } 54 | 55 | impl<'a> Texture<'a> { 56 | /// Loads a texture by name. 57 | pub fn load(device: &'a Device, name: &'static str) -> Self { 58 | Self { 59 | device: &device, 60 | id: device.load_texture(name) 61 | } 62 | } 63 | } 64 | 65 | impl<'a> Drop for Texture<'a> { 66 | fn drop(&mut self) { 67 | self.device.unload_texture(self.id) 68 | } 69 | } 70 | 71 | /// Identifies a rectangular section of a [`Texture`] used as an individual sprite. 72 | #[derive(Clone, Copy)] 73 | pub struct Sprite<'a> { 74 | texture: &'a Texture<'a>, 75 | uv_min: (u32, u32), 76 | uv_max: (u32, u32), 77 | } 78 | 79 | /// A game entity, drawn using a single [`Sprite`]. 80 | pub struct Entity<'a> { 81 | sprite: Sprite<'a>, 82 | pos: (f32, f32), 83 | vel: (f32, f32), 84 | } 85 | 86 | /// Encapsulates the game state at a particular moment. 87 | pub struct Game<'a> { 88 | device: &'a Device, 89 | background: &'a Texture<'a>, 90 | entities: Vec>, 91 | } 92 | 93 | impl<'a> Application for Game<'a> { 94 | ... 95 | } 96 | ``` 97 | 98 | Now, let's just create an instance of `Game` and pass it to `run_app`: 99 | 100 | ```rust 101 | fn main() { 102 | let device = Device::new(); 103 | let background = Texture::load(&device, "background.png"); 104 | let atlas = Texture::load(&device, "atlas.png"); 105 | let player_sprite = Sprite { 106 | texture: &atlas, 107 | uv_min: (0, 0), 108 | uv_max: (64, 64) 109 | }; 110 | let goombler_sprite = Sprite { 111 | texture: &atlas, 112 | uv_min: (64, 0), 113 | uv_max: (128, 64) 114 | }; 115 | run_app(Game { 116 | device: &device, 117 | background: &background, 118 | entities: vec![ 119 | Entity { 120 | sprite: player_sprite, 121 | pos: (0.0, 0.0), 122 | vel: (10.0, 0.0) 123 | }, 124 | Entity { 125 | sprite: goombler_sprite, 126 | pos: (200.0, 0.0), 127 | vel: (-10.0, 0.0) 128 | }, 129 | Entity { 130 | sprite: goombler_sprite, 131 | pos: (400.0, 0.0), 132 | vel: (-10.0, 0.0) 133 | } 134 | ] 135 | }); 136 | } 137 | ``` 138 | 139 | Just compile and we should be good to go! 140 | 141 | ... 142 | 143 | ... 144 | 145 | ... 146 | 147 | Uh oh 148 | 149 | ``` 150 | error[E0597]: `device` does not live long enough 151 | error[E0597]: `device` does not live long enough 152 | error[E0597]: `atlas` does not live long enough 153 | error[E0597]: `atlas` does not live long enough 154 | error[E0597]: `device` does not live long enough 155 | error[E0597]: `background` does not live long enough 156 | ``` 157 | 158 | Looks like we missed the `'static` bound on `run_app`. This is actually a pretty big problem. On 159 | some platforms (e.g. web), the event loop doesn't start until after the main entry point 160 | returns. That means we can't have any stack-allocated data persisting between events. 161 | 162 | This calls our entire reference-based design into question. In times not long ago, we would 163 | be reaching for [`Arc`](https://doc.rust-lang.org/std/sync/struct.Arc.html) or 164 | [`Box::leak`](https://doc.rust-lang.org/std/boxed/struct.Box.html#method.leak). But now we have a 165 | new tool at our disposal: Fortify! 166 | 167 | Instead of passing `run_app` a `Game<'a>`, we can pass it a `Fortify>`. Let's assume 168 | the application framework was kind enough to provide the following implementation: 169 | 170 | ```rust 171 | impl Application for Fortify 172 | where 173 | for<'a> T: Lower<'a>, 174 | for<'a> >::Target: Application, 175 | { 176 | fn draw(&self) { 177 | self.borrow().draw() 178 | } 179 | 180 | fn update(&mut self, step: f32) { 181 | self.with_mut(|app| app.update(step)) 182 | } 183 | } 184 | ``` 185 | 186 | *(It's okay if it doesn't. It just saves us the trouble of creating a wrapper type and doing it 187 | ourselves)* 188 | 189 | Now, lets bundle up all of our game's resources using the `fortify!` macro and try again: 190 | 191 | ```rust 192 | fn main() { 193 | run_app(fortify! { 194 | let device = Device::new(); 195 | let background = Texture::load(&device, "background.png"); 196 | let atlas = Texture::load(&device, "atlas.png"); 197 | let player_sprite = Sprite { 198 | texture: &atlas, 199 | uv_min: (0, 0), 200 | uv_max: (64, 64) 201 | }; 202 | let goombler_sprite = Sprite { 203 | texture: &atlas, 204 | uv_min: (64, 0), 205 | uv_max: (128, 64) 206 | }; 207 | yield Game { 208 | device: &device, 209 | background: &background, 210 | entities: vec![ 211 | Entity { 212 | sprite: player_sprite, 213 | pos: (0.0, 0.0), 214 | vel: (10.0, 0.0) 215 | }, 216 | Entity { 217 | sprite: goombler_sprite, 218 | pos: (200.0, 0.0), 219 | vel: (-10.0, 0.0) 220 | }, 221 | Entity { 222 | sprite: goombler_sprite, 223 | pos: (400.0, 0.0), 224 | vel: (-10.0, 0.0) 225 | } 226 | ] 227 | }; 228 | }); 229 | } 230 | ``` 231 | 232 | Success! With a minor reorganization of the setup code, we were able to promote stack-allocated 233 | variables into long-lived resources. Check out the full code for this example 234 | [here](https://github.com/dzamkov/fortify/blob/master/tests/game.rs). -------------------------------------------------------------------------------- /tests/game.rs: -------------------------------------------------------------------------------- 1 | #![allow(unused_must_use)] 2 | use fortify::*; 3 | use std::cell::Cell; 4 | use std::fmt::Write; 5 | use std::sync::{Arc, Mutex}; 6 | 7 | /// An interface to a graphics device used for drawing and managing textures. 8 | pub struct Device { 9 | log: Arc>, 10 | next_texture_id: Cell, 11 | } 12 | 13 | impl Device { 14 | /// Creates a new [`Device`]. 15 | pub fn new(log: Arc>) -> Self { 16 | writeln!(log.lock().unwrap(), "Initializing device"); 17 | Device { 18 | log, 19 | next_texture_id: Cell::new(0), 20 | } 21 | } 22 | 23 | /// Loads a texture by name. 24 | pub fn load_texture(&self, name: &'static str) -> u32 { 25 | let id = self.next_texture_id.get(); 26 | self.next_texture_id.set(id + 1); 27 | writeln!(self.log.lock().unwrap(), "Loading \"{}\" as texture {}", name, id); 28 | id 29 | } 30 | 31 | /// Unloads a texture. 32 | pub fn unload_texture(&self, id: u32) { 33 | writeln!(self.log.lock().unwrap(), "Unloading texture {}", id); 34 | } 35 | 36 | /// Draws a rectangular section of a [`Texture`] to the screen. 37 | pub fn draw_texture<'a>( 38 | &self, 39 | id: u32, 40 | uv_min: (u32, u32), 41 | uv_max: (u32, u32), 42 | pos: (f32, f32), 43 | ) { 44 | writeln!( 45 | self.log.lock().unwrap(), 46 | "Drawing texture {} from {:?} to {:?} at {:?}", 47 | id, 48 | uv_min, 49 | uv_max, 50 | pos 51 | ); 52 | } 53 | } 54 | 55 | impl Drop for Device { 56 | fn drop(&mut self) { 57 | writeln!(self.log.lock().unwrap(), "Destroying device"); 58 | } 59 | } 60 | 61 | /// Identifies a texture resource owned by a [`Device`]. 62 | pub struct Texture<'a> { 63 | device: &'a Device, 64 | id: u32 65 | } 66 | 67 | impl<'a> Texture<'a> { 68 | /// Loads a texture by name. 69 | pub fn load(device: &'a Device, name: &'static str) -> Self { 70 | Self { 71 | device, 72 | id: device.load_texture(name) 73 | } 74 | } 75 | } 76 | 77 | impl<'a> Drop for Texture<'a> { 78 | fn drop(&mut self) { 79 | self.device.unload_texture(self.id) 80 | } 81 | } 82 | 83 | /// Identifies a rectangular section of a [`Texture`] used as an individual sprite (multiple 84 | /// sprites are typically packed into the same texture to improve performance). 85 | #[derive(Clone, Copy)] 86 | pub struct Sprite<'a> { 87 | texture: &'a Texture<'a>, 88 | uv_min: (u32, u32), 89 | uv_max: (u32, u32), 90 | } 91 | 92 | /// A game entity, drawn using a single [`Sprite`]. 93 | pub struct Entity<'a> { 94 | sprite: Sprite<'a>, 95 | pos: (f32, f32), 96 | vel: (f32, f32), 97 | } 98 | 99 | /// Encapsulates the game state at a particular moment. 100 | #[derive(Lower)] 101 | pub struct Game<'a> { 102 | device: &'a Device, 103 | background: &'a Texture<'a>, 104 | entities: Vec>, 105 | } 106 | 107 | /// An interactive application drawn on a two-dimensional surface. 108 | pub trait Application { 109 | /// Draws the UI for the application. 110 | fn draw(&self); 111 | 112 | /// Updates the state of the application in response to the passage of time. 113 | fn update(&mut self, step: f32); 114 | } 115 | 116 | impl<'a> Application for Game<'a> { 117 | fn draw(&self) { 118 | self.device 119 | .draw_texture(self.background.id, (0, 0), (1024, 1024), (0.0, 0.0)); 120 | for entity in self.entities.iter() { 121 | let sprite = &entity.sprite; 122 | self.device 123 | .draw_texture(sprite.texture.id, sprite.uv_min, sprite.uv_max, entity.pos); 124 | } 125 | } 126 | 127 | fn update(&mut self, step: f32) { 128 | for entity in self.entities.iter_mut() { 129 | let (pos_x, pos_y) = &mut entity.pos; 130 | let (vel_x, vel_y) = entity.vel; 131 | *pos_x += vel_x * step; 132 | *pos_y += vel_y * step; 133 | } 134 | } 135 | } 136 | 137 | // This implementation allows a "fortified" application to be used as an application itself. 138 | impl Application for Fortify 139 | where 140 | for<'a> T: Lower<'a>, 141 | for<'a> >::Target: Application, 142 | { 143 | fn draw(&self) { 144 | self.borrow().draw() 145 | } 146 | 147 | fn update(&mut self, step: f32) { 148 | self.with_mut(|app| app.update(step)) 149 | } 150 | } 151 | 152 | /// Runs an [`Application`]. 153 | pub fn run_app(mut app: impl Application + 'static) { 154 | for _ in 0..5 { 155 | app.draw(); 156 | app.update(1.0); 157 | } 158 | } 159 | 160 | fn run(log: Arc>) { 161 | run_app(fortify! { 162 | let device = Device::new(log); 163 | let background = Texture::load(&device, "background.png"); 164 | let atlas = Texture::load(&device, "atlas.png"); 165 | let player_sprite = Sprite { 166 | texture: &atlas, 167 | uv_min: (0, 0), 168 | uv_max: (64, 64) 169 | }; 170 | let goombler_sprite = Sprite { 171 | texture: &atlas, 172 | uv_min: (64, 0), 173 | uv_max: (128, 64) 174 | }; 175 | yield Game { 176 | device: &device, 177 | background: &background, 178 | entities: vec![ 179 | Entity { 180 | sprite: player_sprite, 181 | pos: (0.0, 0.0), 182 | vel: (10.0, 0.0) 183 | }, 184 | Entity { 185 | sprite: goombler_sprite, 186 | pos: (200.0, 0.0), 187 | vel: (-10.0, 0.0) 188 | }, 189 | Entity { 190 | sprite: goombler_sprite, 191 | pos: (400.0, 0.0), 192 | vel: (-10.0, 0.0) 193 | } 194 | ] 195 | }; 196 | }); 197 | } 198 | 199 | #[test] 200 | fn test() { 201 | // Run program while logging output 202 | let log = Arc::new(Mutex::new(String::new())); 203 | run(log.clone()); 204 | let output = log.lock().unwrap(); 205 | 206 | // Prepare expected output 207 | let mut e = String::new(); 208 | writeln!(&mut e, "Initializing device"); 209 | writeln!(&mut e, "Loading \"background.png\" as texture 0"); 210 | writeln!(&mut e, "Loading \"atlas.png\" as texture 1"); 211 | writeln!(&mut e, "Drawing texture 0 from (0, 0) to (1024, 1024) at (0.0, 0.0)"); 212 | writeln!(&mut e, "Drawing texture 1 from (0, 0) to (64, 64) at (0.0, 0.0)"); 213 | writeln!(&mut e, "Drawing texture 1 from (64, 0) to (128, 64) at (200.0, 0.0)"); 214 | writeln!(&mut e, "Drawing texture 1 from (64, 0) to (128, 64) at (400.0, 0.0)"); 215 | writeln!(&mut e, "Drawing texture 0 from (0, 0) to (1024, 1024) at (0.0, 0.0)"); 216 | writeln!(&mut e, "Drawing texture 1 from (0, 0) to (64, 64) at (10.0, 0.0)"); 217 | writeln!(&mut e, "Drawing texture 1 from (64, 0) to (128, 64) at (190.0, 0.0)"); 218 | writeln!(&mut e, "Drawing texture 1 from (64, 0) to (128, 64) at (390.0, 0.0)"); 219 | writeln!(&mut e, "Drawing texture 0 from (0, 0) to (1024, 1024) at (0.0, 0.0)"); 220 | writeln!(&mut e, "Drawing texture 1 from (0, 0) to (64, 64) at (20.0, 0.0)"); 221 | writeln!(&mut e, "Drawing texture 1 from (64, 0) to (128, 64) at (180.0, 0.0)"); 222 | writeln!(&mut e, "Drawing texture 1 from (64, 0) to (128, 64) at (380.0, 0.0)"); 223 | writeln!(&mut e, "Drawing texture 0 from (0, 0) to (1024, 1024) at (0.0, 0.0)"); 224 | writeln!(&mut e, "Drawing texture 1 from (0, 0) to (64, 64) at (30.0, 0.0)"); 225 | writeln!(&mut e, "Drawing texture 1 from (64, 0) to (128, 64) at (170.0, 0.0)"); 226 | writeln!(&mut e, "Drawing texture 1 from (64, 0) to (128, 64) at (370.0, 0.0)"); 227 | writeln!(&mut e, "Drawing texture 0 from (0, 0) to (1024, 1024) at (0.0, 0.0)"); 228 | writeln!(&mut e, "Drawing texture 1 from (0, 0) to (64, 64) at (40.0, 0.0)"); 229 | writeln!(&mut e, "Drawing texture 1 from (64, 0) to (128, 64) at (160.0, 0.0)"); 230 | writeln!(&mut e, "Drawing texture 1 from (64, 0) to (128, 64) at (360.0, 0.0)"); 231 | writeln!(&mut e, "Unloading texture 1"); 232 | writeln!(&mut e, "Unloading texture 0"); 233 | writeln!(&mut e, "Destroying device"); 234 | 235 | // Compare 236 | assert_eq!(output.as_str(), e.as_str()); 237 | } 238 | --------------------------------------------------------------------------------