├── .github ├── dependabot.yml └── workflows │ └── ci.yml ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── bench ├── .gitignore ├── Cargo.lock ├── Cargo.toml └── benches │ └── iter_benchmark.rs ├── expand ├── .gitignore ├── Cargo.lock ├── Cargo.toml ├── expand.bash └── src │ └── bin │ ├── example1.expanded.rs │ ├── example1.rs │ ├── example2.expanded.rs │ ├── example2.rs │ ├── example3.expanded.rs │ ├── example3.rs │ ├── example4.expanded.rs │ └── example4.rs ├── src └── lib.rs └── tests ├── lib.rs └── lib_no_std.rs /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | timezone: Asia/Tokyo 8 | open-pull-requests-limit: 99 9 | reviewers: [ nwtgck ] 10 | assignees: [ nwtgck ] 11 | - package-ecosystem: cargo 12 | directory: "/bench" 13 | schedule: 14 | interval: daily 15 | timezone: Asia/Tokyo 16 | open-pull-requests-limit: 99 17 | reviewers: [ nwtgck ] 18 | assignees: [ nwtgck ] 19 | - package-ecosystem: github-actions 20 | directory: "/" 21 | schedule: 22 | interval: daily 23 | timezone: Asia/Tokyo 24 | open-pull-requests-limit: 99 25 | reviewers: [ nwtgck ] 26 | assignees: [ nwtgck ] 27 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push] 4 | 5 | env: 6 | RUST_VERSION: 1.75.0 7 | 8 | jobs: 9 | cargo_fmt: 10 | runs-on: ubuntu-22.04 11 | steps: 12 | - run: rustup default ${RUST_VERSION} 13 | - run: rustup component add rustfmt 14 | - uses: actions/checkout@v4 15 | - run: cargo fmt --all -- --check 16 | 17 | cargo_test: 18 | strategy: 19 | matrix: 20 | runs-on: 21 | - ubuntu-22.04 22 | - macOS-13 23 | runs-on: ${{ matrix.runs-on }} 24 | steps: 25 | - run: rustup default ${RUST_VERSION} 26 | - uses: actions/checkout@v4 27 | - run: cargo test -- --color=always --nocapture 28 | - run: cargo test --release -- --color=always --nocapture 29 | - run: rustup +nightly-2024-01-31 component add miri 30 | - run: cargo +nightly-2024-01-31 miri test -- --color=always --nocapture 31 | 32 | # MSRV (minimum supported Rust version) 33 | cargo_test_msrv: 34 | runs-on: ubuntu-22.04 35 | steps: 36 | - run: rustup default 1.63.0 37 | - uses: actions/checkout@v4 38 | - run: cargo test -- --color=always --nocapture 39 | 40 | cargo_bench: 41 | runs-on: ubuntu-22.04 42 | steps: 43 | - run: rustup default ${RUST_VERSION} 44 | - uses: actions/checkout@v4 45 | - run: cd bench && cargo bench 46 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "addr2line" 7 | version = "0.21.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" 10 | dependencies = [ 11 | "gimli", 12 | ] 13 | 14 | [[package]] 15 | name = "adler" 16 | version = "1.0.2" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" 19 | 20 | [[package]] 21 | name = "auto_enums" 22 | version = "0.8.7" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "9c170965892137a3a9aeb000b4524aa3cc022a310e709d848b6e1cdce4ab4781" 25 | dependencies = [ 26 | "derive_utils", 27 | "proc-macro2", 28 | "quote", 29 | "syn", 30 | ] 31 | 32 | [[package]] 33 | name = "autocfg" 34 | version = "1.1.0" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 37 | 38 | [[package]] 39 | name = "backtrace" 40 | version = "0.3.69" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" 43 | dependencies = [ 44 | "addr2line", 45 | "cc", 46 | "cfg-if", 47 | "libc", 48 | "miniz_oxide", 49 | "object", 50 | "rustc-demangle", 51 | ] 52 | 53 | [[package]] 54 | name = "cc" 55 | version = "1.0.83" 56 | source = "registry+https://github.com/rust-lang/crates.io-index" 57 | checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" 58 | dependencies = [ 59 | "libc", 60 | ] 61 | 62 | [[package]] 63 | name = "cfg-if" 64 | version = "1.0.0" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 67 | 68 | [[package]] 69 | name = "derive_utils" 70 | version = "0.15.0" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "ccfae181bab5ab6c5478b2ccb69e4c68a02f8c3ec72f6616bfec9dbc599d2ee0" 73 | dependencies = [ 74 | "proc-macro2", 75 | "quote", 76 | "syn", 77 | ] 78 | 79 | [[package]] 80 | name = "futures" 81 | version = "0.3.31" 82 | source = "registry+https://github.com/rust-lang/crates.io-index" 83 | checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" 84 | dependencies = [ 85 | "futures-channel", 86 | "futures-core", 87 | "futures-executor", 88 | "futures-io", 89 | "futures-sink", 90 | "futures-task", 91 | "futures-util", 92 | ] 93 | 94 | [[package]] 95 | name = "futures-channel" 96 | version = "0.3.31" 97 | source = "registry+https://github.com/rust-lang/crates.io-index" 98 | checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" 99 | dependencies = [ 100 | "futures-core", 101 | "futures-sink", 102 | ] 103 | 104 | [[package]] 105 | name = "futures-core" 106 | version = "0.3.31" 107 | source = "registry+https://github.com/rust-lang/crates.io-index" 108 | checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" 109 | 110 | [[package]] 111 | name = "futures-executor" 112 | version = "0.3.31" 113 | source = "registry+https://github.com/rust-lang/crates.io-index" 114 | checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" 115 | dependencies = [ 116 | "futures-core", 117 | "futures-task", 118 | "futures-util", 119 | ] 120 | 121 | [[package]] 122 | name = "futures-io" 123 | version = "0.3.31" 124 | source = "registry+https://github.com/rust-lang/crates.io-index" 125 | checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" 126 | 127 | [[package]] 128 | name = "futures-macro" 129 | version = "0.3.31" 130 | source = "registry+https://github.com/rust-lang/crates.io-index" 131 | checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" 132 | dependencies = [ 133 | "proc-macro2", 134 | "quote", 135 | "syn", 136 | ] 137 | 138 | [[package]] 139 | name = "futures-sink" 140 | version = "0.3.31" 141 | source = "registry+https://github.com/rust-lang/crates.io-index" 142 | checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" 143 | 144 | [[package]] 145 | name = "futures-task" 146 | version = "0.3.31" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" 149 | 150 | [[package]] 151 | name = "futures-util" 152 | version = "0.3.31" 153 | source = "registry+https://github.com/rust-lang/crates.io-index" 154 | checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" 155 | dependencies = [ 156 | "futures-channel", 157 | "futures-core", 158 | "futures-io", 159 | "futures-macro", 160 | "futures-sink", 161 | "futures-task", 162 | "memchr", 163 | "pin-project-lite", 164 | "pin-utils", 165 | "slab", 166 | ] 167 | 168 | [[package]] 169 | name = "gimli" 170 | version = "0.28.1" 171 | source = "registry+https://github.com/rust-lang/crates.io-index" 172 | checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" 173 | 174 | [[package]] 175 | name = "libc" 176 | version = "0.2.152" 177 | source = "registry+https://github.com/rust-lang/crates.io-index" 178 | checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" 179 | 180 | [[package]] 181 | name = "memchr" 182 | version = "2.7.1" 183 | source = "registry+https://github.com/rust-lang/crates.io-index" 184 | checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" 185 | 186 | [[package]] 187 | name = "miniz_oxide" 188 | version = "0.7.1" 189 | source = "registry+https://github.com/rust-lang/crates.io-index" 190 | checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" 191 | dependencies = [ 192 | "adler", 193 | ] 194 | 195 | [[package]] 196 | name = "object" 197 | version = "0.32.2" 198 | source = "registry+https://github.com/rust-lang/crates.io-index" 199 | checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" 200 | dependencies = [ 201 | "memchr", 202 | ] 203 | 204 | [[package]] 205 | name = "pin-project-lite" 206 | version = "0.2.13" 207 | source = "registry+https://github.com/rust-lang/crates.io-index" 208 | checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" 209 | 210 | [[package]] 211 | name = "pin-utils" 212 | version = "0.1.0" 213 | source = "registry+https://github.com/rust-lang/crates.io-index" 214 | checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" 215 | 216 | [[package]] 217 | name = "proc-macro2" 218 | version = "1.0.76" 219 | source = "registry+https://github.com/rust-lang/crates.io-index" 220 | checksum = "95fc56cda0b5c3325f5fbbd7ff9fda9e02bb00bb3dac51252d2f1bfa1cb8cc8c" 221 | dependencies = [ 222 | "unicode-ident", 223 | ] 224 | 225 | [[package]] 226 | name = "quote" 227 | version = "1.0.35" 228 | source = "registry+https://github.com/rust-lang/crates.io-index" 229 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 230 | dependencies = [ 231 | "proc-macro2", 232 | ] 233 | 234 | [[package]] 235 | name = "rustc-demangle" 236 | version = "0.1.23" 237 | source = "registry+https://github.com/rust-lang/crates.io-index" 238 | checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" 239 | 240 | [[package]] 241 | name = "slab" 242 | version = "0.4.9" 243 | source = "registry+https://github.com/rust-lang/crates.io-index" 244 | checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" 245 | dependencies = [ 246 | "autocfg", 247 | ] 248 | 249 | [[package]] 250 | name = "stacklover" 251 | version = "0.1.0" 252 | dependencies = [ 253 | "auto_enums", 254 | "futures", 255 | "tokio", 256 | ] 257 | 258 | [[package]] 259 | name = "syn" 260 | version = "2.0.58" 261 | source = "registry+https://github.com/rust-lang/crates.io-index" 262 | checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" 263 | dependencies = [ 264 | "proc-macro2", 265 | "quote", 266 | "unicode-ident", 267 | ] 268 | 269 | [[package]] 270 | name = "tokio" 271 | version = "1.38.1" 272 | source = "registry+https://github.com/rust-lang/crates.io-index" 273 | checksum = "eb2caba9f80616f438e09748d5acda951967e1ea58508ef53d9c6402485a46df" 274 | dependencies = [ 275 | "backtrace", 276 | "pin-project-lite", 277 | "tokio-macros", 278 | ] 279 | 280 | [[package]] 281 | name = "tokio-macros" 282 | version = "2.3.0" 283 | source = "registry+https://github.com/rust-lang/crates.io-index" 284 | checksum = "5f5ae998a069d4b5aba8ee9dad856af7d520c3699e6159b185c2acd48155d39a" 285 | dependencies = [ 286 | "proc-macro2", 287 | "quote", 288 | "syn", 289 | ] 290 | 291 | [[package]] 292 | name = "unicode-ident" 293 | version = "1.0.12" 294 | source = "registry+https://github.com/rust-lang/crates.io-index" 295 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 296 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "stacklover" 3 | version = "0.1.0" 4 | edition = "2021" 5 | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 7 | 8 | [dependencies] 9 | 10 | [dev-dependencies] 11 | tokio = { version = "1.38", features = ["rt", "macros", "time"] } 12 | futures = "0.3" 13 | auto_enums = "0.8" 14 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2024 Ryo Ota 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 | # stacklover 2 | [![CI](https://github.com/nwtgck/stacklover-rust/actions/workflows/ci.yml/badge.svg)](https://github.com/nwtgck/stacklover-rust/actions/workflows/ci.yml) 3 | 4 | Zero-cost type for stack without complicated type or Box 5 | 6 | ## Why? 7 | 8 | Rust requires concrete types in struct fields. Here is an example that creates an iterator and puts it into a struct. Its type is super super long and hard to maintain since the type is changed by its creation logic. Also, some types like closures can not be written out. 9 | 10 | ```rust 11 | use std::convert::identity; 12 | use std::sync::{Arc, Mutex}; 13 | 14 | // Super long and complicated type!! 15 | type IteratorI32 = core::iter::Chain< 16 | core::iter::TakeWhile< 17 | core::iter::Map, fn(i32) -> i32>, 18 | fn(&i32) -> bool, 19 | >, 20 | core::iter::FlatMap< 21 | core::iter::Chain< 22 | core::iter::Map, fn(char) -> i32>, 23 | core::array::IntoIter, 24 | >, 25 | [i32; 2], 26 | fn(i32) -> [i32; 2], 27 | >, 28 | >; 29 | // (Here is the end of type defintion!) 30 | 31 | struct MyService { 32 | iter: Mutex>, 33 | } 34 | 35 | impl MyService { 36 | fn put_iter(&self, message: &str) { 37 | // Create an iterator 38 | let iter: IteratorI32 = (1..) 39 | .map(identity:: i32>(|x| x * 3)) // NOTE: An extra identity function needed. Without this, an error "expected fn pointer, found closure" occurrs. 40 | .take_while(identity:: bool>(|x| *x < 10)) // NOTE: An extra identity function needed. 41 | .chain( 42 | "HELLO" 43 | .chars() 44 | .map(identity:: i32>(|c| c as i32)) // NOTE: An extra identity function needed. 45 | .chain([message.len() as i32]) 46 | .flat_map(identity:: [i32; 2]>(|i| [i, i - 65])), // NOTE: An extra identity function needed. 47 | ); 48 | // Put the iterator 49 | self.iter.lock().unwrap().replace(iter); 50 | } 51 | 52 | // ... 53 | } 54 | ``` 55 | 56 | A simple solution is to use `Box` as shown below and allocate the iterator on the heap. 57 | 58 | ```rust 59 | use std::sync::{Arc, Mutex}; 60 | 61 | type IteratorI32 = Box + Send + 'static>; 62 | 63 | struct MyService { 64 | iter: Mutex>, 65 | } 66 | 67 | impl MyService { 68 | fn put_iter(&self, message: &str) { 69 | // Create an iterator 70 | let iter: IteratorI32 = Box::new( 71 | (1..).map(|x| x * 3).take_while(|x| *x < 10).chain( // no extra identity function needed at all 72 | "HELLO" 73 | .chars() 74 | .map(|c| c as i32) 75 | .chain([message.len() as i32]) 76 | .flat_map(|i| [i, i - 65]), 77 | ), 78 | ); 79 | // Put the iterator 80 | self.iter.lock().unwrap().replace(iter); 81 | } 82 | 83 | // ... 84 | } 85 | ``` 86 | 87 | However, we sometimes avoid unnecessary heap allocations and prefer stack allocations. Stack allocation may allow us to inlining functions or static dispatches and improve performance. 88 | 89 | In order to avoid unnecessary `Box` and complicated type, `stacklover::define_struct!` macro is created. You can write the same logic without `Box` or complicated type as below. 90 | 91 | ```rust 92 | use std::sync::{Arc, Mutex}; 93 | 94 | stacklover::define_struct! { 95 | IteratorI32, 96 | fn (message: &str) -> impl Iterator { 97 | (1..).map(|x| x * 3).take_while(|x| *x < 10).chain( 98 | "HELLO" 99 | .chars() 100 | .map(|c| c as i32) 101 | .chain([message.len() as i32]) 102 | .flat_map(|i| [i, i - 65]), 103 | ) 104 | }, 105 | impls = (Send, Sync), 106 | } 107 | 108 | struct MyService { 109 | iter: Mutex>, 110 | } 111 | 112 | impl MyService { 113 | fn put_iter(&self, message: &str) { 114 | // Create an iterator 115 | let iter: IteratorI32 = IteratorI32::new(message); 116 | // Put the iterator 117 | self.iter.lock().unwrap().replace(iter); 118 | } 119 | 120 | // ... 121 | } 122 | ``` 123 | 124 | stacklover also works with no_std. 125 | 126 | ## Performance 127 | 128 | Performance with `stacklover` is the same as with bare type. 129 | 130 | * bare: `impl Iterator` 131 | * boxed: `Box>` 132 | * stacklover 133 | 134 | Here is a result of [iter_benchmark](bench/benches/iter_benchmark.rs) in [GitHub Actions](https://github.com/nwtgck/stacklover-rust/actions/workflows/ci.yml). 135 | 136 | ```txt 137 | iterator sum/bare time: [12.329 ns 12.487 ns 12.670 ns] 138 | iterator sum/boxed time: [348.60 ms 352.41 ms 356.41 ms] 139 | iterator sum/stacklover time: [11.494 ns 11.657 ns 11.819 ns] 140 | ``` 141 | 142 | Note that the time unit of "boxed" is ms not ns. Investigation is underway to know why Box is too slow. 143 | 144 | Data size of struct created by `stacklover` is the same as bare type: 145 | https://github.com/nwtgck/stacklover-rust/blob/796c2dcff68032dbbc064314018565cb21d8c94f/tests/lib.rs#L19 146 | 147 | 148 | ## How to use 149 | 150 | Add `stacklover` to the `Cargo.toml`. 151 | 152 | ### Dependency 153 | 154 | ```toml 155 | [dependencies] 156 | stacklover = { git = "https://github.com/nwtgck/stacklover-rust.git", rev = "efbe91996e5554a9bf7bf3c35bd67c4ed2770866" } 157 | ``` 158 | 159 | ### Simple example 160 | Create by `YourStruct::new()` and access its inner value by `.as_ref()`, `.as_mut()`, `.into_inner()` and `.as_pin_mut()`. Here is an example. 161 | 162 | ```rust 163 | use std::fmt::Debug; 164 | 165 | fn main() { 166 | // Use `define_struct!` everywhere `struct YourStruct;` can be used. 167 | stacklover::define_struct! { 168 | // Struct name to be defined. 169 | IteratorI32, 170 | // Note that the function below has no name. 171 | fn (i: i32) -> impl Iterator + Debug { 172 | (1..i).map(|x| x * 3).take_while(|x| *x < 10) 173 | }, 174 | impls = (Send, Sync, Debug), 175 | } 176 | 177 | // Use `IteratorI32` instead of complicated type. 178 | let mut x: IteratorI32 = IteratorI32::new(10); 179 | 180 | println!("x={:?}", x); 181 | // Output: 182 | // x=TakeWhile { iter: Map { iter: 1..10 }, flag: false } 183 | 184 | println!("size_hint={:?}", x.as_ref().size_hint()); 185 | // Output: 186 | // size_hint=(0, Some(9)) 187 | 188 | println!("next={:?}", x.as_mut().next()); 189 | // Output: 190 | // next=Some(3) 191 | 192 | // Get `Iterator` by .into_inner() 193 | let iter /* : impl Iterator */ = x.into_inner(); 194 | for i in iter { 195 | println!("i={}", i) 196 | } 197 | // Output: 198 | // i=6 199 | // i=9 200 | } 201 | ``` 202 | 203 | ### Async function example 204 | Async function can be used as well. 205 | 206 | ```rust 207 | #[tokio::main] 208 | async fn main() { 209 | stacklover::define_struct! { 210 | IteratorI32, 211 | // async function 212 | async fn (i: i32) -> impl Iterator { 213 | (1..i).map(|x| x * 3).take_while(|x| *x < 10) 214 | }, 215 | impls = (Send, Sync), 216 | } 217 | 218 | let x: IteratorI32 = IteratorI32::new(10).await; // .await used 219 | let iter /* : impl Iterator */ = x.into_inner(); 220 | for i in iter { 221 | println!("i={}", i) 222 | } 223 | // Output: 224 | // i=3 225 | // i=6 226 | // i=9 227 | } 228 | ``` 229 | 230 | ### Wrapped type example 231 | You can store the wrapped type `T` in `Result`. Here is an example using a creation function that returns `Result>`, but the struct `IteratorI32` stores `impl Iterator<...>`. 232 | 233 | ```rust 234 | stacklover::define_struct! { 235 | IteratorI32, 236 | // The function below returns Result but the inner value should be Iterator, not Result. 237 | fn (i: i32) -> Result, std::io::Error> { 238 | let iter = (1..i).map(|x| x * 3).take_while(|x| *x < 10); 239 | Ok(iter) 240 | }, 241 | // Specify parameters in the following order. 242 | impls = (Send, Sync), 243 | inner_type = impl Iterator, 244 | wrapped_type = Result<__Inner__, std::io::Error>, // Type paramter `__Inner__` is predefined in the macro. 245 | to_wrapped_struct = |result, inner_to_struct| { result.map(inner_to_struct) }, 246 | } 247 | 248 | // Use `IteratorI32` instead of complicated type. 249 | let result: Result = IteratorI32::new(10); 250 | // Get IteratorI32 from Result by ?. 251 | let x: IteratorI32 = result?; 252 | ``` 253 | 254 | Here is an example with more nested type and async function. 255 | 256 | ```rust 257 | stacklover::define_struct! { 258 | IteratorI32, 259 | async fn (i: i32) -> Result<(impl Iterator, String, f32), std::io::Error> { 260 | let iter = (1..i).map(|x| x * 3).take_while(|x| *x < 10); 261 | Ok((iter, "hello".to_owned(), 3.14)) 262 | }, 263 | // Specify parameters in the following order. 264 | impls = (Send, Sync), 265 | inner_type = impl Iterator, 266 | wrapped_type = Result<(__Inner__, String, f32), std::io::Error>, // Type paramter `__Inner__` is predefined in the macro. 267 | to_wrapped_struct = |result, inner_to_struct| { result.map(|(iter, s, f)| (inner_to_struct(iter), s, f)) }, 268 | } 269 | 270 | let result: Result<(IteratorI32, String, f32), std::io::Error> = IteratorI32::new(10).await; 271 | // Get a tuple with IteratorI32 from Result by ?. 272 | let (iter, s, f): (IteratorI32, String, f32) = result?; 273 | ``` 274 | 275 | ### Implement trait example 276 | 277 | In `impls = (...)` you can specify the following traits. 278 | * [Send](https://doc.rust-lang.org/std/marker/trait.Send.html) 279 | * [Sync](https://doc.rust-lang.org/std/marker/trait.Sync.html) 280 | * [Unpin](https://doc.rust-lang.org/std/marker/trait.Unpin.html) 281 | * [UnwindSafe](https://doc.rust-lang.org/std/panic/trait.UnwindSafe.html) 282 | * [RefUnwindSafe](https://doc.rust-lang.org/std/panic/trait.RefUnwindSafe.html) 283 | * [PartialEq](https://doc.rust-lang.org/std/cmp/trait.PartialEq.html) 284 | * [Eq](https://doc.rust-lang.org/std/cmp/trait.Eq.html) 285 | * [PartialOrd](https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html) 286 | * [Ord](https://doc.rust-lang.org/std/cmp/trait.Ord.html) 287 | * [Clone](https://doc.rust-lang.org/std/clone/trait.Clone.html) 288 | * [Copy](https://doc.rust-lang.org/core/marker/trait.Copy.html) 289 | * [Hash](https://doc.rust-lang.org/std/hash/trait.Hash.html) 290 | * [Debug](https://doc.rust-lang.org/std/fmt/trait.Debug.html) 291 | 292 | At compile time, it is asserted whether the inner type implements the trait in `impls = (...)`. 293 | 294 | ### Using attributes - auto_enums 295 | Attributes can be used. Here is an example using [`auto_enums`](https://github.com/taiki-e/auto_enums), which allows us to return different types without heap allocations. 296 | 297 | ```rust 298 | fn main() { 299 | stacklover::define_struct! { 300 | AutoEnumIterator, 301 | #[auto_enums::auto_enum(Iterator)] 302 | fn (x: i32) -> impl Iterator { 303 | match x { 304 | 0 => 1..10, 305 | _ => vec![5, 10].into_iter(), 306 | } 307 | }, 308 | impls = (), 309 | } 310 | 311 | let x1: AutoEnumIterator = AutoEnumIterator::new(0); 312 | let iter1 /* : impl Iterator */ = x1.into_inner(); 313 | println!("iter1={:?}", iter1.collect::>()); 314 | // Output: iter1=[1, 2, 3, 4, 5, 6, 7, 8, 9] 315 | 316 | let x2: AutoEnumIterator = AutoEnumIterator::new(4); 317 | let iter2 /* : impl Iterator */ = x2.into_inner(); 318 | println!("iter2={:?}", iter2.collect::>()); 319 | // Output: iter2=[5, 10] 320 | } 321 | ``` 322 | 323 | ### Define separately for IDE and formatter 324 | You can define the creation function separately. This may allow you to have better IDE and formatter support. 325 | ```rust 326 | stacklover::define_struct! { 327 | IteratorI32, 328 | fn (message: &str) -> impl Iterator { 329 | // Call the function 330 | create_iter_i32(message) 331 | }, 332 | impls = (), 333 | } 334 | 335 | // Define a creation function separately 336 | fn create_iter_i32(message: &str) -> impl Iterator { 337 | (1..).map(|x| x * 3).take_while(|x| *x < 10).chain( 338 | "HELLO" 339 | .chars() 340 | .map(|c| c as i32) 341 | .chain([message.len() as i32]) 342 | .flat_map(|i| [i, i - 65]), 343 | ) 344 | } 345 | ``` 346 | 347 | ## How it works 348 | 349 | Read [example1.expanded.rs](expand/src/bin/example1.expanded.rs) expanded from [example1.rs](expand/src/bin/example1.rs). 350 | -------------------------------------------------------------------------------- /bench/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /bench/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "aho-corasick" 7 | version = "1.1.2" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" 10 | dependencies = [ 11 | "memchr", 12 | ] 13 | 14 | [[package]] 15 | name = "anes" 16 | version = "0.1.6" 17 | source = "registry+https://github.com/rust-lang/crates.io-index" 18 | checksum = "4b46cbb362ab8752921c97e041f5e366ee6297bd428a31275b9fcf1e380f7299" 19 | 20 | [[package]] 21 | name = "anstyle" 22 | version = "1.0.4" 23 | source = "registry+https://github.com/rust-lang/crates.io-index" 24 | checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" 25 | 26 | [[package]] 27 | name = "autocfg" 28 | version = "1.1.0" 29 | source = "registry+https://github.com/rust-lang/crates.io-index" 30 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 31 | 32 | [[package]] 33 | name = "bitflags" 34 | version = "2.4.2" 35 | source = "registry+https://github.com/rust-lang/crates.io-index" 36 | checksum = "ed570934406eb16438a4e976b1b4500774099c13b8cb96eec99f620f05090ddf" 37 | 38 | [[package]] 39 | name = "bumpalo" 40 | version = "3.14.0" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" 43 | 44 | [[package]] 45 | name = "cast" 46 | version = "0.3.0" 47 | source = "registry+https://github.com/rust-lang/crates.io-index" 48 | checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" 49 | 50 | [[package]] 51 | name = "cfg-if" 52 | version = "1.0.0" 53 | source = "registry+https://github.com/rust-lang/crates.io-index" 54 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 55 | 56 | [[package]] 57 | name = "ciborium" 58 | version = "0.2.2" 59 | source = "registry+https://github.com/rust-lang/crates.io-index" 60 | checksum = "42e69ffd6f0917f5c029256a24d0161db17cea3997d185db0d35926308770f0e" 61 | dependencies = [ 62 | "ciborium-io", 63 | "ciborium-ll", 64 | "serde", 65 | ] 66 | 67 | [[package]] 68 | name = "ciborium-io" 69 | version = "0.2.2" 70 | source = "registry+https://github.com/rust-lang/crates.io-index" 71 | checksum = "05afea1e0a06c9be33d539b876f1ce3692f4afea2cb41f740e7743225ed1c757" 72 | 73 | [[package]] 74 | name = "ciborium-ll" 75 | version = "0.2.2" 76 | source = "registry+https://github.com/rust-lang/crates.io-index" 77 | checksum = "57663b653d948a338bfb3eeba9bb2fd5fcfaecb9e199e87e1eda4d9e8b240fd9" 78 | dependencies = [ 79 | "ciborium-io", 80 | "half", 81 | ] 82 | 83 | [[package]] 84 | name = "clap" 85 | version = "4.4.18" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "1e578d6ec4194633722ccf9544794b71b1385c3c027efe0c55db226fc880865c" 88 | dependencies = [ 89 | "clap_builder", 90 | ] 91 | 92 | [[package]] 93 | name = "clap_builder" 94 | version = "4.4.18" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "4df4df40ec50c46000231c914968278b1eb05098cf8f1b3a518a95030e71d1c7" 97 | dependencies = [ 98 | "anstyle", 99 | "clap_lex", 100 | ] 101 | 102 | [[package]] 103 | name = "clap_lex" 104 | version = "0.6.0" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" 107 | 108 | [[package]] 109 | name = "criterion" 110 | version = "0.5.1" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "f2b12d017a929603d80db1831cd3a24082f8137ce19c69e6447f54f5fc8d692f" 113 | dependencies = [ 114 | "anes", 115 | "cast", 116 | "ciborium", 117 | "clap", 118 | "criterion-plot", 119 | "is-terminal", 120 | "itertools", 121 | "num-traits", 122 | "once_cell", 123 | "oorandom", 124 | "plotters", 125 | "rayon", 126 | "regex", 127 | "serde", 128 | "serde_derive", 129 | "serde_json", 130 | "tinytemplate", 131 | "walkdir", 132 | ] 133 | 134 | [[package]] 135 | name = "criterion-plot" 136 | version = "0.5.0" 137 | source = "registry+https://github.com/rust-lang/crates.io-index" 138 | checksum = "6b50826342786a51a89e2da3a28f1c32b06e387201bc2d19791f622c673706b1" 139 | dependencies = [ 140 | "cast", 141 | "itertools", 142 | ] 143 | 144 | [[package]] 145 | name = "crossbeam-deque" 146 | version = "0.8.5" 147 | source = "registry+https://github.com/rust-lang/crates.io-index" 148 | checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" 149 | dependencies = [ 150 | "crossbeam-epoch", 151 | "crossbeam-utils", 152 | ] 153 | 154 | [[package]] 155 | name = "crossbeam-epoch" 156 | version = "0.9.18" 157 | source = "registry+https://github.com/rust-lang/crates.io-index" 158 | checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" 159 | dependencies = [ 160 | "crossbeam-utils", 161 | ] 162 | 163 | [[package]] 164 | name = "crossbeam-utils" 165 | version = "0.8.19" 166 | source = "registry+https://github.com/rust-lang/crates.io-index" 167 | checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" 168 | 169 | [[package]] 170 | name = "crunchy" 171 | version = "0.2.2" 172 | source = "registry+https://github.com/rust-lang/crates.io-index" 173 | checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" 174 | 175 | [[package]] 176 | name = "either" 177 | version = "1.9.0" 178 | source = "registry+https://github.com/rust-lang/crates.io-index" 179 | checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" 180 | 181 | [[package]] 182 | name = "errno" 183 | version = "0.3.8" 184 | source = "registry+https://github.com/rust-lang/crates.io-index" 185 | checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" 186 | dependencies = [ 187 | "libc", 188 | "windows-sys", 189 | ] 190 | 191 | [[package]] 192 | name = "half" 193 | version = "2.3.1" 194 | source = "registry+https://github.com/rust-lang/crates.io-index" 195 | checksum = "bc52e53916c08643f1b56ec082790d1e86a32e58dc5268f897f313fbae7b4872" 196 | dependencies = [ 197 | "cfg-if", 198 | "crunchy", 199 | ] 200 | 201 | [[package]] 202 | name = "hermit-abi" 203 | version = "0.3.4" 204 | source = "registry+https://github.com/rust-lang/crates.io-index" 205 | checksum = "5d3d0e0f38255e7fa3cf31335b3a56f05febd18025f4db5ef7a0cfb4f8da651f" 206 | 207 | [[package]] 208 | name = "is-terminal" 209 | version = "0.4.10" 210 | source = "registry+https://github.com/rust-lang/crates.io-index" 211 | checksum = "0bad00257d07be169d870ab665980b06cdb366d792ad690bf2e76876dc503455" 212 | dependencies = [ 213 | "hermit-abi", 214 | "rustix", 215 | "windows-sys", 216 | ] 217 | 218 | [[package]] 219 | name = "itertools" 220 | version = "0.10.5" 221 | source = "registry+https://github.com/rust-lang/crates.io-index" 222 | checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" 223 | dependencies = [ 224 | "either", 225 | ] 226 | 227 | [[package]] 228 | name = "itoa" 229 | version = "1.0.10" 230 | source = "registry+https://github.com/rust-lang/crates.io-index" 231 | checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" 232 | 233 | [[package]] 234 | name = "js-sys" 235 | version = "0.3.67" 236 | source = "registry+https://github.com/rust-lang/crates.io-index" 237 | checksum = "9a1d36f1235bc969acba30b7f5990b864423a6068a10f7c90ae8f0112e3a59d1" 238 | dependencies = [ 239 | "wasm-bindgen", 240 | ] 241 | 242 | [[package]] 243 | name = "libc" 244 | version = "0.2.152" 245 | source = "registry+https://github.com/rust-lang/crates.io-index" 246 | checksum = "13e3bf6590cbc649f4d1a3eefc9d5d6eb746f5200ffb04e5e142700b8faa56e7" 247 | 248 | [[package]] 249 | name = "linux-raw-sys" 250 | version = "0.4.13" 251 | source = "registry+https://github.com/rust-lang/crates.io-index" 252 | checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" 253 | 254 | [[package]] 255 | name = "log" 256 | version = "0.4.20" 257 | source = "registry+https://github.com/rust-lang/crates.io-index" 258 | checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" 259 | 260 | [[package]] 261 | name = "memchr" 262 | version = "2.7.1" 263 | source = "registry+https://github.com/rust-lang/crates.io-index" 264 | checksum = "523dc4f511e55ab87b694dc30d0f820d60906ef06413f93d4d7a1385599cc149" 265 | 266 | [[package]] 267 | name = "num-traits" 268 | version = "0.2.17" 269 | source = "registry+https://github.com/rust-lang/crates.io-index" 270 | checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" 271 | dependencies = [ 272 | "autocfg", 273 | ] 274 | 275 | [[package]] 276 | name = "once_cell" 277 | version = "1.19.0" 278 | source = "registry+https://github.com/rust-lang/crates.io-index" 279 | checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" 280 | 281 | [[package]] 282 | name = "oorandom" 283 | version = "11.1.3" 284 | source = "registry+https://github.com/rust-lang/crates.io-index" 285 | checksum = "0ab1bc2a289d34bd04a330323ac98a1b4bc82c9d9fcb1e66b63caa84da26b575" 286 | 287 | [[package]] 288 | name = "plotters" 289 | version = "0.3.5" 290 | source = "registry+https://github.com/rust-lang/crates.io-index" 291 | checksum = "d2c224ba00d7cadd4d5c660deaf2098e5e80e07846537c51f9cfa4be50c1fd45" 292 | dependencies = [ 293 | "num-traits", 294 | "plotters-backend", 295 | "plotters-svg", 296 | "wasm-bindgen", 297 | "web-sys", 298 | ] 299 | 300 | [[package]] 301 | name = "plotters-backend" 302 | version = "0.3.5" 303 | source = "registry+https://github.com/rust-lang/crates.io-index" 304 | checksum = "9e76628b4d3a7581389a35d5b6e2139607ad7c75b17aed325f210aa91f4a9609" 305 | 306 | [[package]] 307 | name = "plotters-svg" 308 | version = "0.3.5" 309 | source = "registry+https://github.com/rust-lang/crates.io-index" 310 | checksum = "38f6d39893cca0701371e3c27294f09797214b86f1fb951b89ade8ec04e2abab" 311 | dependencies = [ 312 | "plotters-backend", 313 | ] 314 | 315 | [[package]] 316 | name = "proc-macro2" 317 | version = "1.0.78" 318 | source = "registry+https://github.com/rust-lang/crates.io-index" 319 | checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" 320 | dependencies = [ 321 | "unicode-ident", 322 | ] 323 | 324 | [[package]] 325 | name = "quote" 326 | version = "1.0.35" 327 | source = "registry+https://github.com/rust-lang/crates.io-index" 328 | checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" 329 | dependencies = [ 330 | "proc-macro2", 331 | ] 332 | 333 | [[package]] 334 | name = "rayon" 335 | version = "1.8.1" 336 | source = "registry+https://github.com/rust-lang/crates.io-index" 337 | checksum = "fa7237101a77a10773db45d62004a272517633fbcc3df19d96455ede1122e051" 338 | dependencies = [ 339 | "either", 340 | "rayon-core", 341 | ] 342 | 343 | [[package]] 344 | name = "rayon-core" 345 | version = "1.12.1" 346 | source = "registry+https://github.com/rust-lang/crates.io-index" 347 | checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" 348 | dependencies = [ 349 | "crossbeam-deque", 350 | "crossbeam-utils", 351 | ] 352 | 353 | [[package]] 354 | name = "regex" 355 | version = "1.10.3" 356 | source = "registry+https://github.com/rust-lang/crates.io-index" 357 | checksum = "b62dbe01f0b06f9d8dc7d49e05a0785f153b00b2c227856282f671e0318c9b15" 358 | dependencies = [ 359 | "aho-corasick", 360 | "memchr", 361 | "regex-automata", 362 | "regex-syntax", 363 | ] 364 | 365 | [[package]] 366 | name = "regex-automata" 367 | version = "0.4.4" 368 | source = "registry+https://github.com/rust-lang/crates.io-index" 369 | checksum = "3b7fa1134405e2ec9353fd416b17f8dacd46c473d7d3fd1cf202706a14eb792a" 370 | dependencies = [ 371 | "aho-corasick", 372 | "memchr", 373 | "regex-syntax", 374 | ] 375 | 376 | [[package]] 377 | name = "regex-syntax" 378 | version = "0.8.2" 379 | source = "registry+https://github.com/rust-lang/crates.io-index" 380 | checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" 381 | 382 | [[package]] 383 | name = "rustix" 384 | version = "0.38.30" 385 | source = "registry+https://github.com/rust-lang/crates.io-index" 386 | checksum = "322394588aaf33c24007e8bb3238ee3e4c5c09c084ab32bc73890b99ff326bca" 387 | dependencies = [ 388 | "bitflags", 389 | "errno", 390 | "libc", 391 | "linux-raw-sys", 392 | "windows-sys", 393 | ] 394 | 395 | [[package]] 396 | name = "ryu" 397 | version = "1.0.16" 398 | source = "registry+https://github.com/rust-lang/crates.io-index" 399 | checksum = "f98d2aa92eebf49b69786be48e4477826b256916e84a57ff2a4f21923b48eb4c" 400 | 401 | [[package]] 402 | name = "same-file" 403 | version = "1.0.6" 404 | source = "registry+https://github.com/rust-lang/crates.io-index" 405 | checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" 406 | dependencies = [ 407 | "winapi-util", 408 | ] 409 | 410 | [[package]] 411 | name = "serde" 412 | version = "1.0.195" 413 | source = "registry+https://github.com/rust-lang/crates.io-index" 414 | checksum = "63261df402c67811e9ac6def069e4786148c4563f4b50fd4bf30aa370d626b02" 415 | dependencies = [ 416 | "serde_derive", 417 | ] 418 | 419 | [[package]] 420 | name = "serde_derive" 421 | version = "1.0.195" 422 | source = "registry+https://github.com/rust-lang/crates.io-index" 423 | checksum = "46fe8f8603d81ba86327b23a2e9cdf49e1255fb94a4c5f297f6ee0547178ea2c" 424 | dependencies = [ 425 | "proc-macro2", 426 | "quote", 427 | "syn", 428 | ] 429 | 430 | [[package]] 431 | name = "serde_json" 432 | version = "1.0.111" 433 | source = "registry+https://github.com/rust-lang/crates.io-index" 434 | checksum = "176e46fa42316f18edd598015a5166857fc835ec732f5215eac6b7bdbf0a84f4" 435 | dependencies = [ 436 | "itoa", 437 | "ryu", 438 | "serde", 439 | ] 440 | 441 | [[package]] 442 | name = "stacklover" 443 | version = "0.1.0" 444 | 445 | [[package]] 446 | name = "stacklover-bench" 447 | version = "0.0.0" 448 | dependencies = [ 449 | "criterion", 450 | "stacklover", 451 | ] 452 | 453 | [[package]] 454 | name = "syn" 455 | version = "2.0.48" 456 | source = "registry+https://github.com/rust-lang/crates.io-index" 457 | checksum = "0f3531638e407dfc0814761abb7c00a5b54992b849452a0646b7f65c9f770f3f" 458 | dependencies = [ 459 | "proc-macro2", 460 | "quote", 461 | "unicode-ident", 462 | ] 463 | 464 | [[package]] 465 | name = "tinytemplate" 466 | version = "1.2.1" 467 | source = "registry+https://github.com/rust-lang/crates.io-index" 468 | checksum = "be4d6b5f19ff7664e8c98d03e2139cb510db9b0a60b55f8e8709b689d939b6bc" 469 | dependencies = [ 470 | "serde", 471 | "serde_json", 472 | ] 473 | 474 | [[package]] 475 | name = "unicode-ident" 476 | version = "1.0.12" 477 | source = "registry+https://github.com/rust-lang/crates.io-index" 478 | checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" 479 | 480 | [[package]] 481 | name = "walkdir" 482 | version = "2.4.0" 483 | source = "registry+https://github.com/rust-lang/crates.io-index" 484 | checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" 485 | dependencies = [ 486 | "same-file", 487 | "winapi-util", 488 | ] 489 | 490 | [[package]] 491 | name = "wasm-bindgen" 492 | version = "0.2.90" 493 | source = "registry+https://github.com/rust-lang/crates.io-index" 494 | checksum = "b1223296a201415c7fad14792dbefaace9bd52b62d33453ade1c5b5f07555406" 495 | dependencies = [ 496 | "cfg-if", 497 | "wasm-bindgen-macro", 498 | ] 499 | 500 | [[package]] 501 | name = "wasm-bindgen-backend" 502 | version = "0.2.90" 503 | source = "registry+https://github.com/rust-lang/crates.io-index" 504 | checksum = "fcdc935b63408d58a32f8cc9738a0bffd8f05cc7c002086c6ef20b7312ad9dcd" 505 | dependencies = [ 506 | "bumpalo", 507 | "log", 508 | "once_cell", 509 | "proc-macro2", 510 | "quote", 511 | "syn", 512 | "wasm-bindgen-shared", 513 | ] 514 | 515 | [[package]] 516 | name = "wasm-bindgen-macro" 517 | version = "0.2.90" 518 | source = "registry+https://github.com/rust-lang/crates.io-index" 519 | checksum = "3e4c238561b2d428924c49815533a8b9121c664599558a5d9ec51f8a1740a999" 520 | dependencies = [ 521 | "quote", 522 | "wasm-bindgen-macro-support", 523 | ] 524 | 525 | [[package]] 526 | name = "wasm-bindgen-macro-support" 527 | version = "0.2.90" 528 | source = "registry+https://github.com/rust-lang/crates.io-index" 529 | checksum = "bae1abb6806dc1ad9e560ed242107c0f6c84335f1749dd4e8ddb012ebd5e25a7" 530 | dependencies = [ 531 | "proc-macro2", 532 | "quote", 533 | "syn", 534 | "wasm-bindgen-backend", 535 | "wasm-bindgen-shared", 536 | ] 537 | 538 | [[package]] 539 | name = "wasm-bindgen-shared" 540 | version = "0.2.90" 541 | source = "registry+https://github.com/rust-lang/crates.io-index" 542 | checksum = "4d91413b1c31d7539ba5ef2451af3f0b833a005eb27a631cec32bc0635a8602b" 543 | 544 | [[package]] 545 | name = "web-sys" 546 | version = "0.3.67" 547 | source = "registry+https://github.com/rust-lang/crates.io-index" 548 | checksum = "58cd2333b6e0be7a39605f0e255892fd7418a682d8da8fe042fe25128794d2ed" 549 | dependencies = [ 550 | "js-sys", 551 | "wasm-bindgen", 552 | ] 553 | 554 | [[package]] 555 | name = "winapi" 556 | version = "0.3.9" 557 | source = "registry+https://github.com/rust-lang/crates.io-index" 558 | checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" 559 | dependencies = [ 560 | "winapi-i686-pc-windows-gnu", 561 | "winapi-x86_64-pc-windows-gnu", 562 | ] 563 | 564 | [[package]] 565 | name = "winapi-i686-pc-windows-gnu" 566 | version = "0.4.0" 567 | source = "registry+https://github.com/rust-lang/crates.io-index" 568 | checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" 569 | 570 | [[package]] 571 | name = "winapi-util" 572 | version = "0.1.6" 573 | source = "registry+https://github.com/rust-lang/crates.io-index" 574 | checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" 575 | dependencies = [ 576 | "winapi", 577 | ] 578 | 579 | [[package]] 580 | name = "winapi-x86_64-pc-windows-gnu" 581 | version = "0.4.0" 582 | source = "registry+https://github.com/rust-lang/crates.io-index" 583 | checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" 584 | 585 | [[package]] 586 | name = "windows-sys" 587 | version = "0.52.0" 588 | source = "registry+https://github.com/rust-lang/crates.io-index" 589 | checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" 590 | dependencies = [ 591 | "windows-targets", 592 | ] 593 | 594 | [[package]] 595 | name = "windows-targets" 596 | version = "0.52.0" 597 | source = "registry+https://github.com/rust-lang/crates.io-index" 598 | checksum = "8a18201040b24831fbb9e4eb208f8892e1f50a37feb53cc7ff887feb8f50e7cd" 599 | dependencies = [ 600 | "windows_aarch64_gnullvm", 601 | "windows_aarch64_msvc", 602 | "windows_i686_gnu", 603 | "windows_i686_msvc", 604 | "windows_x86_64_gnu", 605 | "windows_x86_64_gnullvm", 606 | "windows_x86_64_msvc", 607 | ] 608 | 609 | [[package]] 610 | name = "windows_aarch64_gnullvm" 611 | version = "0.52.0" 612 | source = "registry+https://github.com/rust-lang/crates.io-index" 613 | checksum = "cb7764e35d4db8a7921e09562a0304bf2f93e0a51bfccee0bd0bb0b666b015ea" 614 | 615 | [[package]] 616 | name = "windows_aarch64_msvc" 617 | version = "0.52.0" 618 | source = "registry+https://github.com/rust-lang/crates.io-index" 619 | checksum = "bbaa0368d4f1d2aaefc55b6fcfee13f41544ddf36801e793edbbfd7d7df075ef" 620 | 621 | [[package]] 622 | name = "windows_i686_gnu" 623 | version = "0.52.0" 624 | source = "registry+https://github.com/rust-lang/crates.io-index" 625 | checksum = "a28637cb1fa3560a16915793afb20081aba2c92ee8af57b4d5f28e4b3e7df313" 626 | 627 | [[package]] 628 | name = "windows_i686_msvc" 629 | version = "0.52.0" 630 | source = "registry+https://github.com/rust-lang/crates.io-index" 631 | checksum = "ffe5e8e31046ce6230cc7215707b816e339ff4d4d67c65dffa206fd0f7aa7b9a" 632 | 633 | [[package]] 634 | name = "windows_x86_64_gnu" 635 | version = "0.52.0" 636 | source = "registry+https://github.com/rust-lang/crates.io-index" 637 | checksum = "3d6fa32db2bc4a2f5abeacf2b69f7992cd09dca97498da74a151a3132c26befd" 638 | 639 | [[package]] 640 | name = "windows_x86_64_gnullvm" 641 | version = "0.52.0" 642 | source = "registry+https://github.com/rust-lang/crates.io-index" 643 | checksum = "1a657e1e9d3f514745a572a6846d3c7aa7dbe1658c056ed9c3344c4109a6949e" 644 | 645 | [[package]] 646 | name = "windows_x86_64_msvc" 647 | version = "0.52.0" 648 | source = "registry+https://github.com/rust-lang/crates.io-index" 649 | checksum = "dff9641d1cd4be8d1a070daf9e3773c5f67e78b4d9d42263020c057706765c04" 650 | -------------------------------------------------------------------------------- /bench/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "stacklover-bench" 3 | edition = "2021" 4 | 5 | [dev-dependencies] 6 | criterion = { version = "0.5", features = ["html_reports"] } 7 | stacklover = { path = ".." } 8 | 9 | [[bench]] 10 | name = "iter_benchmark" 11 | harness = false 12 | -------------------------------------------------------------------------------- /bench/benches/iter_benchmark.rs: -------------------------------------------------------------------------------- 1 | use criterion::{black_box, criterion_group, criterion_main, Criterion}; 2 | 3 | fn iter_i64() -> impl Iterator { 4 | black_box(1..) 5 | .map(|x| x * 3) 6 | .take_while(|x| *x < 999999999) 7 | .chain("HELLO".chars().map(|c| c as i64).flat_map(|i| [i, i - 65])) 8 | } 9 | 10 | fn criterion_benchmark(c: &mut Criterion) { 11 | let mut group = c.benchmark_group("iterator sum"); 12 | group.bench_function("bare", |b| b.iter(|| iter_i64().sum::())); 13 | group.bench_function("boxed", |b| b.iter(|| Box::new(iter_i64()).sum::())); 14 | group.bench_function("stacklover", |b| { 15 | stacklover::define_struct! { 16 | // struct name to be defined 17 | Iterator1, 18 | fn () -> impl Iterator { 19 | iter_i64() 20 | }, 21 | impls = (), 22 | } 23 | b.iter(|| Iterator1::new().into_inner().sum::()) 24 | }); 25 | group.finish(); 26 | } 27 | 28 | criterion_group!(benches, criterion_benchmark); 29 | criterion_main!(benches); 30 | -------------------------------------------------------------------------------- /expand/.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /expand/Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "stacklover" 7 | version = "0.1.0" 8 | 9 | [[package]] 10 | name = "stacklover-expand" 11 | version = "0.0.0" 12 | dependencies = [ 13 | "stacklover", 14 | ] 15 | -------------------------------------------------------------------------------- /expand/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "stacklover-expand" 3 | edition = "2021" 4 | 5 | [dependencies] 6 | stacklover = { path = ".." } 7 | 8 | [[bin]] 9 | name = "example1" 10 | 11 | [[bin]] 12 | name = "example2" 13 | 14 | [[bin]] 15 | name = "example3" 16 | 17 | [[bin]] 18 | name = "example4" 19 | -------------------------------------------------------------------------------- /expand/expand.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -xeu 3 | 4 | cd $(dirname "$0") 5 | 6 | for bin_name in example1 example2 example3 example4 7 | do 8 | cargo expand --bin ${bin_name} > ./src/bin/${bin_name}.expanded.rs 9 | done 10 | -------------------------------------------------------------------------------- /expand/src/bin/example1.expanded.rs: -------------------------------------------------------------------------------- 1 | #![feature(prelude_import)] 2 | #[prelude_import] 3 | use std::prelude::rust_2021::*; 4 | #[macro_use] 5 | extern crate std; 6 | #[repr(transparent)] 7 | struct Iterator1 { 8 | #[doc(hidden)] 9 | __private_inner: ::stacklover::__private_mod::ErasedStorage< 10 | { Iterator1::__SIZE }, 11 | { Iterator1::__ALIGN }, 12 | >, 13 | } 14 | const _: () = { 15 | type __StackloverWrappedType<__Inner__> = __Inner__; 16 | #[inline(always)] 17 | fn __stacklover_create( 18 | dep1: &'static str, 19 | dep2: i32, 20 | ) -> impl Iterator + Clone { 21 | (1..) 22 | .map(|x| x * 3) 23 | .take_while(|x| *x < 20) 24 | .chain("HELLO".chars().map(|c| c as i32).flat_map(|i| [i, i - 65])) 25 | .chain([dep1.len() as i32, dep2]) 26 | } 27 | #[allow(unused)] 28 | #[allow(unreachable_code)] 29 | fn __stacklover_inner_unreachable() -> impl Iterator + Clone { 30 | let __stacklover_inner_to_struct_fn_unreachable = |inner| -> Iterator1 { 31 | ::core::panicking::panic("internal error: entered unreachable code") 32 | }; 33 | let _ = { 34 | let created_value = __stacklover_create( 35 | ::core::panicking::panic("internal error: entered unreachable code"), 36 | ::core::panicking::panic("internal error: entered unreachable code"), 37 | ); 38 | let inner_to_struct = __stacklover_inner_to_struct_fn_unreachable; 39 | inner_to_struct(created_value) 40 | }; 41 | fn __stacklover_fn_param_unreachable(_: impl Fn(T) -> R) -> T { 42 | ::core::panicking::panic("internal error: entered unreachable code") 43 | } 44 | __stacklover_fn_param_unreachable(__stacklover_inner_to_struct_fn_unreachable) 45 | } 46 | impl Iterator1 { 47 | #[inline(always)] 48 | pub fn new(dep1: &'static str, dep2: i32) -> __StackloverWrappedType { 49 | let __stacklover_inner_to_struct_fn = |inner| Self { 50 | __private_inner: unsafe { 51 | ::core::mem::transmute::< 52 | _, 53 | ::stacklover::__private_mod::ErasedStorage< 54 | { Iterator1::__SIZE }, 55 | { Iterator1::__ALIGN }, 56 | >, 57 | >(inner) 58 | }, 59 | }; 60 | { 61 | let created_value = __stacklover_create(dep1, dep2); 62 | let inner_to_struct = __stacklover_inner_to_struct_fn; 63 | inner_to_struct(created_value) 64 | } 65 | } 66 | } 67 | const _: () = { 68 | if !(::core::mem::size_of::() == Iterator1::__SIZE) { 69 | { 70 | ::core::panicking::panic_fmt(format_args!("invalid size")); 71 | } 72 | } 73 | if !(::core::mem::align_of::() == Iterator1::__ALIGN) { 74 | { 75 | ::core::panicking::panic_fmt(format_args!("invalid align")); 76 | } 77 | } 78 | }; 79 | const _: fn() = || { 80 | fn assert_static(_: T) {} 81 | assert_static(__stacklover_inner_unreachable()); 82 | }; 83 | impl Iterator1 { 84 | #[doc(hidden)] 85 | const __SIZE: usize = { 86 | const fn size_of_return_value( 87 | _: &(impl ::core::ops::Fn() -> R), 88 | ) -> usize { 89 | ::core::mem::size_of::() 90 | } 91 | size_of_return_value(&__stacklover_inner_unreachable) 92 | }; 93 | #[doc(hidden)] 94 | const __ALIGN: usize = { 95 | const fn align_of_return_value( 96 | _: &(impl ::core::ops::Fn() -> R), 97 | ) -> usize { 98 | ::core::mem::align_of::() 99 | } 100 | align_of_return_value(&__stacklover_inner_unreachable) 101 | }; 102 | #[inline(always)] 103 | pub fn as_ref(&self) -> &(impl Iterator + Clone) { 104 | if true { 105 | unsafe { 106 | ::core::mem::transmute::< 107 | &::stacklover::__private_mod::ErasedStorage< 108 | { Iterator1::__SIZE }, 109 | { Iterator1::__ALIGN }, 110 | >, 111 | _, 112 | >(&self.__private_inner) 113 | } 114 | } else { 115 | fn ref_unreachable(_self: &S, _: T) -> &T { 116 | ::core::panicking::panic("internal error: entered unreachable code") 117 | } 118 | #[allow(unreachable_code)] 119 | ref_unreachable(self, __stacklover_inner_unreachable()) 120 | } 121 | } 122 | #[inline(always)] 123 | pub fn as_mut(&mut self) -> &mut (impl Iterator + Clone) { 124 | if true { 125 | unsafe { 126 | ::core::mem::transmute::< 127 | &mut ::stacklover::__private_mod::ErasedStorage< 128 | { Iterator1::__SIZE }, 129 | { Iterator1::__ALIGN }, 130 | >, 131 | _, 132 | >(&mut self.__private_inner) 133 | } 134 | } else { 135 | fn mut_unreachable(_self: &mut S, _: T) -> &mut T { 136 | ::core::panicking::panic("internal error: entered unreachable code") 137 | } 138 | #[allow(unreachable_code)] 139 | mut_unreachable(self, __stacklover_inner_unreachable()) 140 | } 141 | } 142 | #[inline(always)] 143 | pub fn into_inner(self) -> impl Iterator + Clone { 144 | if true { 145 | unsafe { ::core::mem::transmute(self) } 146 | } else { 147 | #[allow(unreachable_code)] __stacklover_inner_unreachable() 148 | } 149 | } 150 | #[inline(always)] 151 | pub fn as_pin_mut( 152 | self: ::core::pin::Pin<&mut Self>, 153 | ) -> ::core::pin::Pin<&mut (impl Iterator + Clone)> { 154 | unsafe { self.map_unchecked_mut(Self::as_mut) } 155 | } 156 | } 157 | const _: fn() = || { 158 | fn assert_trait(_: T) {} 159 | assert_trait(__stacklover_inner_unreachable()); 160 | }; 161 | unsafe impl ::core::marker::Send for Iterator1 {} 162 | const _: fn() = || { 163 | fn assert_trait(_: T) {} 164 | assert_trait(__stacklover_inner_unreachable()); 165 | }; 166 | unsafe impl ::core::marker::Sync for Iterator1 {} 167 | impl ::core::clone::Clone for Iterator1 { 168 | fn clone(&self) -> Self { 169 | let cloned = ::core::clone::Clone::clone(Iterator1::as_ref(self)); 170 | Self { 171 | __private_inner: unsafe { 172 | ::core::mem::transmute::< 173 | _, 174 | ::stacklover::__private_mod::ErasedStorage< 175 | { Iterator1::__SIZE }, 176 | { Iterator1::__ALIGN }, 177 | >, 178 | >(cloned) 179 | }, 180 | } 181 | } 182 | } 183 | impl ::core::ops::Drop for Iterator1 { 184 | #[inline(always)] 185 | fn drop(&mut self) { 186 | unsafe { ::core::ptr::drop_in_place(self.as_mut()) } 187 | } 188 | } 189 | }; 190 | fn main() {} 191 | -------------------------------------------------------------------------------- /expand/src/bin/example1.rs: -------------------------------------------------------------------------------- 1 | stacklover::define_struct! { 2 | Iterator1, 3 | fn (dep1: &'static str, dep2: i32) -> impl Iterator + Clone { 4 | (1..) 5 | .map(|x| x * 3) 6 | .take_while(|x| *x < 20) 7 | .chain("HELLO".chars().map(|c| c as i32).flat_map(|i| [i, i - 65])) 8 | .chain([dep1.len() as i32, dep2]) 9 | }, 10 | impls = (Send, Sync, Clone), 11 | } 12 | 13 | // cargo run --bin example1 14 | fn main() {} 15 | -------------------------------------------------------------------------------- /expand/src/bin/example2.expanded.rs: -------------------------------------------------------------------------------- 1 | #![feature(prelude_import)] 2 | #[prelude_import] 3 | use std::prelude::rust_2021::*; 4 | #[macro_use] 5 | extern crate std; 6 | #[repr(transparent)] 7 | struct Iterator1 { 8 | #[doc(hidden)] 9 | __private_inner: ::stacklover::__private_mod::ErasedStorage< 10 | { Iterator1::__SIZE }, 11 | { Iterator1::__ALIGN }, 12 | >, 13 | } 14 | const _: () = { 15 | type __StackloverWrappedType<__Inner__> = __Inner__; 16 | #[inline(always)] 17 | async fn __stacklover_create( 18 | dep1: &'static str, 19 | dep2: i32, 20 | ) -> impl Iterator + Clone { 21 | (1..) 22 | .map(|x| x * 3) 23 | .take_while(|x| *x < 20) 24 | .chain("HELLO".chars().map(|c| c as i32).flat_map(|i| [i, i - 65])) 25 | .chain([dep1.len() as i32, dep2]) 26 | } 27 | #[allow(unused)] 28 | #[allow(unreachable_code)] 29 | fn __stacklover_inner_unreachable() -> impl Iterator + Clone { 30 | fn __stacklover_await_future_unreachable< 31 | T: ::core::future::Future, 32 | O, 33 | >(_: T) -> O { 34 | ::core::panicking::panic("internal error: entered unreachable code") 35 | } 36 | let __stacklover_inner_to_struct_fn_unreachable = |inner| -> Iterator1 { 37 | ::core::panicking::panic("internal error: entered unreachable code") 38 | }; 39 | let _ = { 40 | let created_value = __stacklover_await_future_unreachable( 41 | __stacklover_create( 42 | ::core::panicking::panic("internal error: entered unreachable code"), 43 | ::core::panicking::panic("internal error: entered unreachable code"), 44 | ), 45 | ); 46 | let inner_to_struct = __stacklover_inner_to_struct_fn_unreachable; 47 | inner_to_struct(created_value) 48 | }; 49 | fn __stacklover_fn_param_unreachable(_: impl Fn(T) -> R) -> T { 50 | ::core::panicking::panic("internal error: entered unreachable code") 51 | } 52 | __stacklover_fn_param_unreachable(__stacklover_inner_to_struct_fn_unreachable) 53 | } 54 | impl Iterator1 { 55 | #[inline(always)] 56 | pub async fn new( 57 | dep1: &'static str, 58 | dep2: i32, 59 | ) -> __StackloverWrappedType { 60 | let __stacklover_inner_to_struct_fn = |inner| Self { 61 | __private_inner: unsafe { 62 | ::core::mem::transmute::< 63 | _, 64 | ::stacklover::__private_mod::ErasedStorage< 65 | { Iterator1::__SIZE }, 66 | { Iterator1::__ALIGN }, 67 | >, 68 | >(inner) 69 | }, 70 | }; 71 | { 72 | let created_value = __stacklover_create(dep1, dep2).await; 73 | let inner_to_struct = __stacklover_inner_to_struct_fn; 74 | inner_to_struct(created_value) 75 | } 76 | } 77 | } 78 | const _: () = { 79 | if !(::core::mem::size_of::() == Iterator1::__SIZE) { 80 | { 81 | ::core::panicking::panic_fmt(format_args!("invalid size")); 82 | } 83 | } 84 | if !(::core::mem::align_of::() == Iterator1::__ALIGN) { 85 | { 86 | ::core::panicking::panic_fmt(format_args!("invalid align")); 87 | } 88 | } 89 | }; 90 | const _: fn() = || { 91 | fn assert_static(_: T) {} 92 | assert_static(__stacklover_inner_unreachable()); 93 | }; 94 | impl Iterator1 { 95 | #[doc(hidden)] 96 | const __SIZE: usize = { 97 | const fn size_of_return_value( 98 | _: &(impl ::core::ops::Fn() -> R), 99 | ) -> usize { 100 | ::core::mem::size_of::() 101 | } 102 | size_of_return_value(&__stacklover_inner_unreachable) 103 | }; 104 | #[doc(hidden)] 105 | const __ALIGN: usize = { 106 | const fn align_of_return_value( 107 | _: &(impl ::core::ops::Fn() -> R), 108 | ) -> usize { 109 | ::core::mem::align_of::() 110 | } 111 | align_of_return_value(&__stacklover_inner_unreachable) 112 | }; 113 | #[inline(always)] 114 | pub fn as_ref(&self) -> &(impl Iterator + Clone) { 115 | if true { 116 | unsafe { 117 | ::core::mem::transmute::< 118 | &::stacklover::__private_mod::ErasedStorage< 119 | { Iterator1::__SIZE }, 120 | { Iterator1::__ALIGN }, 121 | >, 122 | _, 123 | >(&self.__private_inner) 124 | } 125 | } else { 126 | fn ref_unreachable(_self: &S, _: T) -> &T { 127 | ::core::panicking::panic("internal error: entered unreachable code") 128 | } 129 | #[allow(unreachable_code)] 130 | ref_unreachable(self, __stacklover_inner_unreachable()) 131 | } 132 | } 133 | #[inline(always)] 134 | pub fn as_mut(&mut self) -> &mut (impl Iterator + Clone) { 135 | if true { 136 | unsafe { 137 | ::core::mem::transmute::< 138 | &mut ::stacklover::__private_mod::ErasedStorage< 139 | { Iterator1::__SIZE }, 140 | { Iterator1::__ALIGN }, 141 | >, 142 | _, 143 | >(&mut self.__private_inner) 144 | } 145 | } else { 146 | fn mut_unreachable(_self: &mut S, _: T) -> &mut T { 147 | ::core::panicking::panic("internal error: entered unreachable code") 148 | } 149 | #[allow(unreachable_code)] 150 | mut_unreachable(self, __stacklover_inner_unreachable()) 151 | } 152 | } 153 | #[inline(always)] 154 | pub fn into_inner(self) -> impl Iterator + Clone { 155 | if true { 156 | unsafe { ::core::mem::transmute(self) } 157 | } else { 158 | #[allow(unreachable_code)] __stacklover_inner_unreachable() 159 | } 160 | } 161 | #[inline(always)] 162 | pub fn as_pin_mut( 163 | self: ::core::pin::Pin<&mut Self>, 164 | ) -> ::core::pin::Pin<&mut (impl Iterator + Clone)> { 165 | unsafe { self.map_unchecked_mut(Self::as_mut) } 166 | } 167 | } 168 | const _: fn() = || { 169 | fn assert_trait(_: T) {} 170 | assert_trait(__stacklover_inner_unreachable()); 171 | }; 172 | unsafe impl ::core::marker::Send for Iterator1 {} 173 | const _: fn() = || { 174 | fn assert_trait(_: T) {} 175 | assert_trait(__stacklover_inner_unreachable()); 176 | }; 177 | unsafe impl ::core::marker::Sync for Iterator1 {} 178 | impl ::core::clone::Clone for Iterator1 { 179 | fn clone(&self) -> Self { 180 | let cloned = ::core::clone::Clone::clone(Iterator1::as_ref(self)); 181 | Self { 182 | __private_inner: unsafe { 183 | ::core::mem::transmute::< 184 | _, 185 | ::stacklover::__private_mod::ErasedStorage< 186 | { Iterator1::__SIZE }, 187 | { Iterator1::__ALIGN }, 188 | >, 189 | >(cloned) 190 | }, 191 | } 192 | } 193 | } 194 | impl ::core::ops::Drop for Iterator1 { 195 | #[inline(always)] 196 | fn drop(&mut self) { 197 | unsafe { ::core::ptr::drop_in_place(self.as_mut()) } 198 | } 199 | } 200 | }; 201 | fn main() {} 202 | -------------------------------------------------------------------------------- /expand/src/bin/example2.rs: -------------------------------------------------------------------------------- 1 | stacklover::define_struct! { 2 | Iterator1, 3 | async fn (dep1: &'static str, dep2: i32) -> impl Iterator + Clone { 4 | (1..) 5 | .map(|x| x * 3) 6 | .take_while(|x| *x < 20) 7 | .chain("HELLO".chars().map(|c| c as i32).flat_map(|i| [i, i - 65])) 8 | .chain([dep1.len() as i32, dep2]) 9 | }, 10 | impls = (Send, Sync, Clone), 11 | } 12 | 13 | // cargo run --bin example2 14 | fn main() {} 15 | -------------------------------------------------------------------------------- /expand/src/bin/example3.expanded.rs: -------------------------------------------------------------------------------- 1 | #![feature(prelude_import)] 2 | #[prelude_import] 3 | use std::prelude::rust_2021::*; 4 | #[macro_use] 5 | extern crate std; 6 | use stacklover::define_struct; 7 | #[repr(transparent)] 8 | struct Iterator1 { 9 | #[doc(hidden)] 10 | __private_inner: ::stacklover::__private_mod::ErasedStorage< 11 | { Iterator1::__SIZE }, 12 | { Iterator1::__ALIGN }, 13 | >, 14 | } 15 | const _: () = { 16 | type __StackloverWrappedType<__Inner__> = Result<__Inner__, std::io::Error>; 17 | #[inline(always)] 18 | fn __stacklover_create( 19 | dep1: &'static str, 20 | dep2: i32, 21 | ) -> Result + Clone, std::io::Error> { 22 | let iter = (1..) 23 | .map(|x| x * 3) 24 | .take_while(|x| *x < 20) 25 | .chain("HELLO".chars().map(|c| c as i32).flat_map(|i| [i, i - 65])) 26 | .chain([dep1.len() as i32, dep2]); 27 | Ok(iter) 28 | } 29 | #[allow(unused)] 30 | #[allow(unreachable_code)] 31 | fn __stacklover_inner_unreachable() -> impl Iterator + Clone { 32 | let __stacklover_inner_to_struct_fn_unreachable = |inner| -> Iterator1 { 33 | ::core::panicking::panic("internal error: entered unreachable code") 34 | }; 35 | let _ = { 36 | let result = __stacklover_create( 37 | ::core::panicking::panic("internal error: entered unreachable code"), 38 | ::core::panicking::panic("internal error: entered unreachable code"), 39 | ); 40 | let inner_to_struct = __stacklover_inner_to_struct_fn_unreachable; 41 | result.map(|inner| inner_to_struct(inner)) 42 | }; 43 | fn __stacklover_fn_param_unreachable(_: impl Fn(T) -> R) -> T { 44 | ::core::panicking::panic("internal error: entered unreachable code") 45 | } 46 | __stacklover_fn_param_unreachable(__stacklover_inner_to_struct_fn_unreachable) 47 | } 48 | impl Iterator1 { 49 | #[inline(always)] 50 | pub fn new(dep1: &'static str, dep2: i32) -> __StackloverWrappedType { 51 | let __stacklover_inner_to_struct_fn = |inner| Self { 52 | __private_inner: unsafe { 53 | ::core::mem::transmute::< 54 | _, 55 | ::stacklover::__private_mod::ErasedStorage< 56 | { Iterator1::__SIZE }, 57 | { Iterator1::__ALIGN }, 58 | >, 59 | >(inner) 60 | }, 61 | }; 62 | { 63 | let result = __stacklover_create(dep1, dep2); 64 | let inner_to_struct = __stacklover_inner_to_struct_fn; 65 | result.map(|inner| inner_to_struct(inner)) 66 | } 67 | } 68 | } 69 | const _: () = { 70 | if !(::core::mem::size_of::() == Iterator1::__SIZE) { 71 | { 72 | ::core::panicking::panic_fmt(format_args!("invalid size")); 73 | } 74 | } 75 | if !(::core::mem::align_of::() == Iterator1::__ALIGN) { 76 | { 77 | ::core::panicking::panic_fmt(format_args!("invalid align")); 78 | } 79 | } 80 | }; 81 | const _: fn() = || { 82 | fn assert_static(_: T) {} 83 | assert_static(__stacklover_inner_unreachable()); 84 | }; 85 | impl Iterator1 { 86 | #[doc(hidden)] 87 | const __SIZE: usize = { 88 | const fn size_of_return_value( 89 | _: &(impl ::core::ops::Fn() -> R), 90 | ) -> usize { 91 | ::core::mem::size_of::() 92 | } 93 | size_of_return_value(&__stacklover_inner_unreachable) 94 | }; 95 | #[doc(hidden)] 96 | const __ALIGN: usize = { 97 | const fn align_of_return_value( 98 | _: &(impl ::core::ops::Fn() -> R), 99 | ) -> usize { 100 | ::core::mem::align_of::() 101 | } 102 | align_of_return_value(&__stacklover_inner_unreachable) 103 | }; 104 | #[inline(always)] 105 | pub fn as_ref(&self) -> &(impl Iterator + Clone) { 106 | if true { 107 | unsafe { 108 | ::core::mem::transmute::< 109 | &::stacklover::__private_mod::ErasedStorage< 110 | { Iterator1::__SIZE }, 111 | { Iterator1::__ALIGN }, 112 | >, 113 | _, 114 | >(&self.__private_inner) 115 | } 116 | } else { 117 | fn ref_unreachable(_self: &S, _: T) -> &T { 118 | ::core::panicking::panic("internal error: entered unreachable code") 119 | } 120 | #[allow(unreachable_code)] 121 | ref_unreachable(self, __stacklover_inner_unreachable()) 122 | } 123 | } 124 | #[inline(always)] 125 | pub fn as_mut(&mut self) -> &mut (impl Iterator + Clone) { 126 | if true { 127 | unsafe { 128 | ::core::mem::transmute::< 129 | &mut ::stacklover::__private_mod::ErasedStorage< 130 | { Iterator1::__SIZE }, 131 | { Iterator1::__ALIGN }, 132 | >, 133 | _, 134 | >(&mut self.__private_inner) 135 | } 136 | } else { 137 | fn mut_unreachable(_self: &mut S, _: T) -> &mut T { 138 | ::core::panicking::panic("internal error: entered unreachable code") 139 | } 140 | #[allow(unreachable_code)] 141 | mut_unreachable(self, __stacklover_inner_unreachable()) 142 | } 143 | } 144 | #[inline(always)] 145 | pub fn into_inner(self) -> impl Iterator + Clone { 146 | if true { 147 | unsafe { ::core::mem::transmute(self) } 148 | } else { 149 | #[allow(unreachable_code)] __stacklover_inner_unreachable() 150 | } 151 | } 152 | #[inline(always)] 153 | pub fn as_pin_mut( 154 | self: ::core::pin::Pin<&mut Self>, 155 | ) -> ::core::pin::Pin<&mut (impl Iterator + Clone)> { 156 | unsafe { self.map_unchecked_mut(Self::as_mut) } 157 | } 158 | } 159 | const _: fn() = || { 160 | fn assert_trait(_: T) {} 161 | assert_trait(__stacklover_inner_unreachable()); 162 | }; 163 | unsafe impl ::core::marker::Send for Iterator1 {} 164 | const _: fn() = || { 165 | fn assert_trait(_: T) {} 166 | assert_trait(__stacklover_inner_unreachable()); 167 | }; 168 | unsafe impl ::core::marker::Sync for Iterator1 {} 169 | impl ::core::clone::Clone for Iterator1 { 170 | fn clone(&self) -> Self { 171 | let cloned = ::core::clone::Clone::clone(Iterator1::as_ref(self)); 172 | Self { 173 | __private_inner: unsafe { 174 | ::core::mem::transmute::< 175 | _, 176 | ::stacklover::__private_mod::ErasedStorage< 177 | { Iterator1::__SIZE }, 178 | { Iterator1::__ALIGN }, 179 | >, 180 | >(cloned) 181 | }, 182 | } 183 | } 184 | } 185 | impl ::core::ops::Drop for Iterator1 { 186 | #[inline(always)] 187 | fn drop(&mut self) { 188 | unsafe { ::core::ptr::drop_in_place(self.as_mut()) } 189 | } 190 | } 191 | }; 192 | fn main() { 193 | let result: Result = Iterator1::new("hello", 10); 194 | for i in result.unwrap().into_inner() { 195 | { 196 | ::std::io::_print(format_args!("i={0}\n", i)); 197 | }; 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /expand/src/bin/example3.rs: -------------------------------------------------------------------------------- 1 | use stacklover::define_struct; 2 | 3 | define_struct! { 4 | Iterator1, 5 | fn (dep1: &'static str, dep2: i32) -> Result + Clone, std::io::Error> { 6 | let iter = (1..) 7 | .map(|x| x * 3) 8 | .take_while(|x| *x < 20) 9 | .chain("HELLO".chars().map(|c| c as i32).flat_map(|i| [i, i - 65])) 10 | .chain([dep1.len() as i32, dep2]); 11 | Ok(iter) 12 | }, 13 | impls = (Send, Sync, Clone), 14 | inner_type = impl Iterator + Clone, 15 | wrapped_type = Result<__Inner__, std::io::Error>, 16 | to_wrapped_struct = |result, inner_to_struct| { result.map(|inner| inner_to_struct(inner)) }, 17 | } 18 | 19 | // cargo run --bin example3 20 | fn main() { 21 | let result: Result = Iterator1::new("hello", 10); 22 | for i in result.unwrap().into_inner() { 23 | println!("i={}", i); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /expand/src/bin/example4.expanded.rs: -------------------------------------------------------------------------------- 1 | #![feature(prelude_import)] 2 | #[prelude_import] 3 | use std::prelude::rust_2021::*; 4 | #[macro_use] 5 | extern crate std; 6 | #[repr(transparent)] 7 | struct I32 { 8 | #[doc(hidden)] 9 | __private_inner: ::stacklover::__private_mod::ErasedStorage< 10 | { I32::__SIZE }, 11 | { I32::__ALIGN }, 12 | >, 13 | } 14 | const _: () = { 15 | type __StackloverWrappedType<__Inner__> = __Inner__; 16 | #[inline(always)] 17 | fn __stacklover_create(dep2: i32) -> i32 { 18 | dep2 19 | } 20 | #[allow(unused)] 21 | #[allow(unreachable_code)] 22 | fn __stacklover_inner_unreachable() -> i32 { 23 | let __stacklover_inner_to_struct_fn_unreachable = |inner| -> I32 { 24 | ::core::panicking::panic("internal error: entered unreachable code") 25 | }; 26 | let _ = { 27 | let created_value = __stacklover_create( 28 | ::core::panicking::panic("internal error: entered unreachable code"), 29 | ); 30 | let inner_to_struct = __stacklover_inner_to_struct_fn_unreachable; 31 | inner_to_struct(created_value) 32 | }; 33 | fn __stacklover_fn_param_unreachable(_: impl Fn(T) -> R) -> T { 34 | ::core::panicking::panic("internal error: entered unreachable code") 35 | } 36 | __stacklover_fn_param_unreachable(__stacklover_inner_to_struct_fn_unreachable) 37 | } 38 | impl I32 { 39 | #[inline(always)] 40 | pub fn new(dep2: i32) -> __StackloverWrappedType { 41 | let __stacklover_inner_to_struct_fn = |inner| Self { 42 | __private_inner: unsafe { 43 | ::core::mem::transmute::< 44 | _, 45 | ::stacklover::__private_mod::ErasedStorage< 46 | { I32::__SIZE }, 47 | { I32::__ALIGN }, 48 | >, 49 | >(inner) 50 | }, 51 | }; 52 | { 53 | let created_value = __stacklover_create(dep2); 54 | let inner_to_struct = __stacklover_inner_to_struct_fn; 55 | inner_to_struct(created_value) 56 | } 57 | } 58 | } 59 | const _: () = { 60 | if !(::core::mem::size_of::() == I32::__SIZE) { 61 | { 62 | ::core::panicking::panic_fmt(format_args!("invalid size")); 63 | } 64 | } 65 | if !(::core::mem::align_of::() == I32::__ALIGN) { 66 | { 67 | ::core::panicking::panic_fmt(format_args!("invalid align")); 68 | } 69 | } 70 | }; 71 | const _: fn() = || { 72 | fn assert_static(_: T) {} 73 | assert_static(__stacklover_inner_unreachable()); 74 | }; 75 | impl I32 { 76 | #[doc(hidden)] 77 | const __SIZE: usize = { 78 | const fn size_of_return_value( 79 | _: &(impl ::core::ops::Fn() -> R), 80 | ) -> usize { 81 | ::core::mem::size_of::() 82 | } 83 | size_of_return_value(&__stacklover_inner_unreachable) 84 | }; 85 | #[doc(hidden)] 86 | const __ALIGN: usize = { 87 | const fn align_of_return_value( 88 | _: &(impl ::core::ops::Fn() -> R), 89 | ) -> usize { 90 | ::core::mem::align_of::() 91 | } 92 | align_of_return_value(&__stacklover_inner_unreachable) 93 | }; 94 | #[inline(always)] 95 | pub fn as_ref(&self) -> &(i32) { 96 | if true { 97 | unsafe { 98 | ::core::mem::transmute::< 99 | &::stacklover::__private_mod::ErasedStorage< 100 | { I32::__SIZE }, 101 | { I32::__ALIGN }, 102 | >, 103 | _, 104 | >(&self.__private_inner) 105 | } 106 | } else { 107 | fn ref_unreachable(_self: &S, _: T) -> &T { 108 | ::core::panicking::panic("internal error: entered unreachable code") 109 | } 110 | #[allow(unreachable_code)] 111 | ref_unreachable(self, __stacklover_inner_unreachable()) 112 | } 113 | } 114 | #[inline(always)] 115 | pub fn as_mut(&mut self) -> &mut (i32) { 116 | if true { 117 | unsafe { 118 | ::core::mem::transmute::< 119 | &mut ::stacklover::__private_mod::ErasedStorage< 120 | { I32::__SIZE }, 121 | { I32::__ALIGN }, 122 | >, 123 | _, 124 | >(&mut self.__private_inner) 125 | } 126 | } else { 127 | fn mut_unreachable(_self: &mut S, _: T) -> &mut T { 128 | ::core::panicking::panic("internal error: entered unreachable code") 129 | } 130 | #[allow(unreachable_code)] 131 | mut_unreachable(self, __stacklover_inner_unreachable()) 132 | } 133 | } 134 | #[inline(always)] 135 | pub fn into_inner(self) -> i32 { 136 | if true { 137 | unsafe { ::core::mem::transmute(self) } 138 | } else { 139 | #[allow(unreachable_code)] __stacklover_inner_unreachable() 140 | } 141 | } 142 | #[inline(always)] 143 | pub fn as_pin_mut( 144 | self: ::core::pin::Pin<&mut Self>, 145 | ) -> ::core::pin::Pin<&mut (i32)> { 146 | unsafe { self.map_unchecked_mut(Self::as_mut) } 147 | } 148 | } 149 | impl ::core::cmp::PartialEq for I32 { 150 | fn eq(&self, other: &Self) -> bool { 151 | ::core::cmp::PartialEq::eq(I32::as_ref(self), I32::as_ref(other)) 152 | } 153 | fn ne(&self, other: &Self) -> bool { 154 | ::core::cmp::PartialEq::ne(I32::as_ref(self), I32::as_ref(other)) 155 | } 156 | } 157 | impl ::core::cmp::Eq for I32 {} 158 | impl ::core::cmp::PartialOrd for I32 { 159 | fn partial_cmp( 160 | &self, 161 | other: &Self, 162 | ) -> ::core::option::Option<::core::cmp::Ordering> { 163 | ::core::cmp::PartialOrd::partial_cmp(I32::as_ref(self), I32::as_ref(other)) 164 | } 165 | fn lt(&self, other: &Self) -> bool { 166 | ::core::cmp::PartialOrd::lt(I32::as_ref(self), I32::as_ref(other)) 167 | } 168 | fn le(&self, other: &Self) -> bool { 169 | ::core::cmp::PartialOrd::le(I32::as_ref(self), I32::as_ref(other)) 170 | } 171 | fn gt(&self, other: &Self) -> bool { 172 | ::core::cmp::PartialOrd::gt(I32::as_ref(self), I32::as_ref(other)) 173 | } 174 | fn ge(&self, other: &Self) -> bool { 175 | ::core::cmp::PartialOrd::ge(I32::as_ref(self), I32::as_ref(other)) 176 | } 177 | } 178 | impl ::core::cmp::Ord for I32 { 179 | fn cmp(&self, other: &Self) -> ::core::cmp::Ordering { 180 | ::core::cmp::Ord::cmp(I32::as_ref(self), I32::as_ref(other)) 181 | } 182 | } 183 | impl ::core::clone::Clone for I32 { 184 | fn clone(&self) -> Self { 185 | let cloned = ::core::clone::Clone::clone(I32::as_ref(self)); 186 | Self { 187 | __private_inner: unsafe { 188 | ::core::mem::transmute::< 189 | _, 190 | ::stacklover::__private_mod::ErasedStorage< 191 | { I32::__SIZE }, 192 | { I32::__ALIGN }, 193 | >, 194 | >(cloned) 195 | }, 196 | } 197 | } 198 | } 199 | impl ::core::hash::Hash for I32 { 200 | fn hash(&self, state: &mut H) { 201 | ::core::hash::Hash::hash(I32::as_ref(self), state) 202 | } 203 | } 204 | impl ::core::fmt::Debug for I32 { 205 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 206 | ::core::fmt::Debug::fmt(I32::as_ref(self), f) 207 | } 208 | } 209 | impl ::core::ops::Drop for I32 { 210 | #[inline(always)] 211 | fn drop(&mut self) { 212 | unsafe { ::core::ptr::drop_in_place(self.as_mut()) } 213 | } 214 | } 215 | }; 216 | fn main() {} 217 | -------------------------------------------------------------------------------- /expand/src/bin/example4.rs: -------------------------------------------------------------------------------- 1 | stacklover::define_struct! { 2 | I32, 3 | fn (dep2: i32) -> i32 { 4 | dep2 5 | }, 6 | impls = ( PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Debug ), 7 | } 8 | 9 | // cargo run --bin example4 10 | fn main() {} 11 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_export] 2 | macro_rules! define_struct { 3 | // not async create 4 | ( 5 | $struct_name:ident, 6 | $(#[$attrs:meta])* 7 | fn ( $( $param:ident: $param_ty:ty ),* ) -> $create_fn_return_type:ty { $($create_fn_body:tt)* }, 8 | impls = ( $($derive_trait:ident),* $(,)? ) $(,)? 9 | ) => { 10 | $crate::define_struct!( 11 | $struct_name, 12 | $(#[$attrs])* 13 | fn ( $( $param: $param_ty ),* ) -> $create_fn_return_type { $($create_fn_body)* }, 14 | impls = ( $($derive_trait),* ), 15 | inner_type = $create_fn_return_type, 16 | wrapped_type = __Inner__, 17 | to_wrapped_struct = |created_value, inner_to_struct| { inner_to_struct(created_value) }, 18 | ); 19 | }; 20 | // not async create 21 | ( 22 | $struct_name:ident, 23 | $(#[$attrs:meta])* 24 | fn ( $( $param:ident: $param_ty:ty ),* ) -> $create_fn_return_type:ty { $($create_fn_body:tt)* }, 25 | impls = ( $($derive_trait:ident),* $(,)? ), 26 | inner_type = $inner_type:ty, 27 | // wrapped_type should include __Inner__ 28 | wrapped_type = $wrapped_type:ty, 29 | to_wrapped_struct = |$created_value:ident, $inner_to_struct_fn:ident| { $($to_wrapped_struct_body:tt)* } $(,)? 30 | ) => { 31 | $crate::__define_struct!($struct_name); 32 | 33 | const _: () = { 34 | type __StackloverWrappedType<__Inner__> = $wrapped_type; 35 | 36 | // NOTE: prefix "__" is for avoiding name confliction. The function body should not use the function name because it will be accidentally a recursive function. 37 | #[inline(always)] 38 | $(#[$attrs])* 39 | fn __stacklover_create( $($param: $param_ty ),* ) -> $create_fn_return_type { 40 | $($create_fn_body)* 41 | } 42 | 43 | #[allow(unused)] 44 | #[allow(unreachable_code)] 45 | fn __stacklover_inner_unreachable() -> $inner_type { 46 | let __stacklover_inner_to_struct_fn_unreachable = |inner| -> $struct_name { ::core::unreachable!() }; 47 | let _ = { 48 | let $created_value = __stacklover_create( $( $crate::__ident_to_unreachable!($param) ),* ); 49 | let $inner_to_struct_fn = __stacklover_inner_to_struct_fn_unreachable; 50 | // For type inference of __stacklover_inner_to_struct_fn_unreachable 51 | $($to_wrapped_struct_body)* 52 | }; 53 | fn __stacklover_fn_param_unreachable(_: impl Fn(T) -> R) -> T { 54 | ::core::unreachable!() 55 | } 56 | __stacklover_fn_param_unreachable(__stacklover_inner_to_struct_fn_unreachable) 57 | } 58 | 59 | impl $struct_name { 60 | #[inline(always)] 61 | pub fn new( $( $param: $param_ty ),* ) -> __StackloverWrappedType { 62 | let __stacklover_inner_to_struct_fn = |inner| Self { 63 | __private_inner: unsafe { 64 | ::core::mem::transmute::<_, $crate::__private_mod::ErasedStorage<{ $struct_name::__SIZE }, { $struct_name::__ALIGN }>>(inner) 65 | }, 66 | }; 67 | { 68 | let $created_value = __stacklover_create( $($param),* ); 69 | let $inner_to_struct_fn = __stacklover_inner_to_struct_fn; 70 | $($to_wrapped_struct_body)* 71 | } 72 | } 73 | } 74 | 75 | $crate::__assert_and_as_ref_and_as_mut_and_into_inner_and_drop!($struct_name, $inner_type); 76 | $crate::__impl_traits!($struct_name, $($derive_trait)*); 77 | }; 78 | }; 79 | // async create 80 | ( 81 | $struct_name:ident, 82 | $(#[$attrs:meta])* 83 | $async:ident fn ( $( $param:ident: $param_ty:ty ),* ) -> $create_fn_return_type:ty { $($create_fn_body:tt)* }, 84 | impls = ( $($derive_trait:ident),* $(,)? ) $(,)? 85 | ) => { 86 | $crate::define_struct!( 87 | $struct_name, 88 | $(#[$attrs])* 89 | $async fn ( $( $param: $param_ty ),* ) -> $create_fn_return_type { $($create_fn_body)* }, 90 | impls = ( $($derive_trait),* ), 91 | inner_type = $create_fn_return_type, 92 | wrapped_type = __Inner__, 93 | to_wrapped_struct = |created_value, inner_to_struct| { inner_to_struct(created_value) }, 94 | ); 95 | }; 96 | // async create 97 | ( 98 | $struct_name:ident, 99 | $(#[$attrs:meta])* 100 | $async:ident fn ( $( $param:ident: $param_ty:ty ),* ) -> $create_fn_return_type:ty { $($create_fn_body:tt)* }, 101 | impls = ( $($derive_trait:ident),* $(,)? ), 102 | inner_type = $inner_type:ty, 103 | // wrapped_type should include __Inner__ 104 | wrapped_type = $wrapped_type:ty, 105 | to_wrapped_struct = |$created_value:ident, $inner_to_struct_fn:ident| { $($to_wrapped_struct_body:tt)* } $(,)? 106 | ) => { 107 | $crate::__define_struct!($struct_name); 108 | 109 | const _: () = { 110 | type __StackloverWrappedType<__Inner__> = $wrapped_type; 111 | 112 | #[inline(always)] 113 | $(#[$attrs])* 114 | $async fn __stacklover_create( $($param: $param_ty ),* ) -> $create_fn_return_type { 115 | $($create_fn_body)* 116 | } 117 | 118 | #[allow(unused)] 119 | #[allow(unreachable_code)] 120 | fn __stacklover_inner_unreachable() -> $inner_type { 121 | fn __stacklover_await_future_unreachable, O>(_: T) -> O { 122 | ::core::unreachable!() 123 | } 124 | let __stacklover_inner_to_struct_fn_unreachable = |inner| -> $struct_name { ::core::unreachable!() }; 125 | let _ = { 126 | let $created_value = __stacklover_await_future_unreachable(__stacklover_create( $( $crate::__ident_to_unreachable!($param) ),* )); 127 | let $inner_to_struct_fn = __stacklover_inner_to_struct_fn_unreachable; 128 | // For type inference of __stacklover_inner_to_struct_fn_unreachable 129 | $($to_wrapped_struct_body)* 130 | }; 131 | fn __stacklover_fn_param_unreachable(_: impl Fn(T) -> R) -> T { 132 | ::core::unreachable!() 133 | } 134 | __stacklover_fn_param_unreachable(__stacklover_inner_to_struct_fn_unreachable) 135 | } 136 | 137 | impl $struct_name { 138 | #[inline(always)] 139 | pub $async fn new( $($param: $param_ty ),* ) -> __StackloverWrappedType { 140 | let __stacklover_inner_to_struct_fn = |inner| Self { 141 | __private_inner: unsafe { 142 | ::core::mem::transmute::<_, $crate::__private_mod::ErasedStorage<{ $struct_name::__SIZE }, { $struct_name::__ALIGN }>>(inner) 143 | }, 144 | }; 145 | { 146 | let $created_value = __stacklover_create( $($param),* ).await; 147 | let $inner_to_struct_fn = __stacklover_inner_to_struct_fn; 148 | $($to_wrapped_struct_body)* 149 | } 150 | } 151 | } 152 | 153 | $crate::__assert_and_as_ref_and_as_mut_and_into_inner_and_drop!($struct_name, $inner_type); 154 | $crate::__impl_traits!($struct_name, $($derive_trait)*); 155 | }; 156 | }; 157 | } 158 | 159 | #[doc(hidden)] 160 | #[macro_export] 161 | macro_rules! __ident_to_unreachable { 162 | ( $x:ident ) => { 163 | ::core::unreachable!() 164 | }; 165 | } 166 | 167 | #[doc(hidden)] 168 | #[macro_export] 169 | macro_rules! __define_struct { 170 | ( $struct_name:ident ) => { 171 | #[repr(transparent)] 172 | struct $struct_name { 173 | #[doc(hidden)] 174 | __private_inner: $crate::__private_mod::ErasedStorage< 175 | { $struct_name::__SIZE }, 176 | { $struct_name::__ALIGN }, 177 | >, 178 | } 179 | }; 180 | } 181 | 182 | #[doc(hidden)] 183 | #[macro_export] 184 | macro_rules! __assert_and_as_ref_and_as_mut_and_into_inner_and_drop { 185 | ( $struct_name:ident, $inner_type:ty ) => { 186 | // At compile time, assert struct size and align equal to those of the inner type. 187 | const _: () = { 188 | ::core::assert!( 189 | ::core::mem::size_of::<$struct_name>() == $struct_name::__SIZE, 190 | "invalid size" 191 | ); 192 | ::core::assert!( 193 | ::core::mem::align_of::<$struct_name>() == $struct_name::__ALIGN, 194 | "invalid align" 195 | ); 196 | }; 197 | 198 | const _: fn() = || { 199 | fn assert_static(_: T) {} 200 | assert_static(__stacklover_inner_unreachable()); 201 | }; 202 | 203 | impl $struct_name { 204 | #[doc(hidden)] 205 | const __SIZE: usize = { 206 | const fn size_of_return_value(_: &(impl ::core::ops::Fn() -> R)) -> usize { 207 | ::core::mem::size_of::() 208 | } 209 | size_of_return_value(&__stacklover_inner_unreachable) 210 | }; 211 | 212 | #[doc(hidden)] 213 | const __ALIGN: usize = { 214 | const fn align_of_return_value(_: &(impl ::core::ops::Fn() -> R)) -> usize { 215 | ::core::mem::align_of::() 216 | } 217 | align_of_return_value(&__stacklover_inner_unreachable) 218 | }; 219 | 220 | #[inline(always)] 221 | pub fn as_ref(&self) -> &($inner_type) { 222 | if true { 223 | unsafe { 224 | ::core::mem::transmute::< 225 | &$crate::__private_mod::ErasedStorage< 226 | { $struct_name::__SIZE }, 227 | { $struct_name::__ALIGN }, 228 | >, 229 | _, 230 | >(&self.__private_inner) 231 | } 232 | } else { 233 | // _self for lifetime 234 | fn ref_unreachable(_self: &S, _: T) -> &T { 235 | ::core::unreachable!() 236 | } 237 | #[allow(unreachable_code)] 238 | ref_unreachable(self, __stacklover_inner_unreachable()) 239 | } 240 | } 241 | 242 | #[inline(always)] 243 | pub fn as_mut(&mut self) -> &mut ($inner_type) { 244 | if true { 245 | unsafe { 246 | ::core::mem::transmute::< 247 | &mut $crate::__private_mod::ErasedStorage< 248 | { $struct_name::__SIZE }, 249 | { $struct_name::__ALIGN }, 250 | >, 251 | _, 252 | >(&mut self.__private_inner) 253 | } 254 | } else { 255 | // _self for lifetime 256 | fn mut_unreachable(_self: &mut S, _: T) -> &mut T { 257 | ::core::unreachable!() 258 | } 259 | #[allow(unreachable_code)] 260 | mut_unreachable(self, __stacklover_inner_unreachable()) 261 | } 262 | } 263 | 264 | #[inline(always)] 265 | pub fn into_inner(self) -> $inner_type { 266 | if true { 267 | unsafe { ::core::mem::transmute(self) } 268 | } else { 269 | #[allow(unreachable_code)] 270 | __stacklover_inner_unreachable() 271 | } 272 | } 273 | 274 | #[inline(always)] 275 | pub fn as_pin_mut( 276 | self: ::core::pin::Pin<&mut Self>, 277 | ) -> ::core::pin::Pin<&mut ($inner_type)> { 278 | unsafe { self.map_unchecked_mut(Self::as_mut) } 279 | } 280 | } 281 | }; 282 | } 283 | 284 | #[doc(hidden)] 285 | #[macro_export] 286 | macro_rules! __impl_traits { 287 | // only implement Drop when no copy impl is requested. Not implementing Drop in this case 288 | // can not leak a value, since it is asserted that the inner type is also Copy, so has trivial Drop. 289 | ( $struct_name:ident, # NO_DROP ) => { 290 | }; 291 | ( $struct_name:ident, ) => { 292 | impl ::core::ops::Drop for $struct_name { 293 | #[inline(always)] 294 | fn drop(&mut self) { 295 | unsafe { ::core::ptr::drop_in_place(self.as_mut()) } 296 | } 297 | } 298 | }; 299 | // auto traits: https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits 300 | ( $struct_name:ident, Send $($xs:tt)* ) => { 301 | const _: fn () = || { 302 | fn assert_trait(_: T) {} 303 | assert_trait(__stacklover_inner_unreachable()); 304 | }; 305 | unsafe impl ::core::marker::Send for $struct_name {} 306 | $crate::__impl_traits!($struct_name, $($xs)*); 307 | }; 308 | ( $struct_name:ident, Sync $($xs:tt)* ) => { 309 | const _: fn () = || { 310 | fn assert_trait(_: T) {} 311 | assert_trait(__stacklover_inner_unreachable()); 312 | }; 313 | unsafe impl ::core::marker::Sync for $struct_name {} 314 | $crate::__impl_traits!($struct_name, $($xs)*); 315 | }; 316 | ( $struct_name:ident, Unpin $($xs:tt)* ) => { 317 | const _: fn () = || { 318 | fn assert_trait(_: T) {} 319 | assert_trait(__stacklover_inner_unreachable()); 320 | }; 321 | impl ::core::marker::Unpin for $struct_name {} 322 | $crate::__impl_traits!($struct_name, $($xs)*); 323 | }; 324 | ( $struct_name:ident, UnwindSafe $($xs:tt)* ) => { 325 | const _: fn () = || { 326 | fn assert_trait(_: T) {} 327 | assert_trait(__stacklover_inner_unreachable()); 328 | }; 329 | impl ::core::panic::UnwindSafe for $struct_name {} 330 | $crate::__impl_traits!($struct_name, $($xs)*); 331 | }; 332 | ( $struct_name:ident, RefUnwindSafe $($xs:tt)* ) => { 333 | const _: fn () = || { 334 | fn assert_trait(_: T) {} 335 | assert_trait(__stacklover_inner_unreachable()); 336 | }; 337 | impl ::core::panic::RefUnwindSafe for $struct_name {} 338 | $crate::__impl_traits!($struct_name, $($xs)*); 339 | }; 340 | // other traits 341 | ( $struct_name:ident, PartialEq $($xs:tt)* ) => { 342 | impl ::core::cmp::PartialEq for $struct_name { 343 | fn eq(&self, other: &Self) -> bool { 344 | ::core::cmp::PartialEq::eq($struct_name::as_ref(self), $struct_name::as_ref(other)) 345 | } 346 | 347 | fn ne(&self, other: &Self) -> bool { 348 | ::core::cmp::PartialEq::ne($struct_name::as_ref(self), $struct_name::as_ref(other)) 349 | } 350 | } 351 | $crate::__impl_traits!($struct_name, $($xs)*); 352 | }; 353 | ( $struct_name:ident, Eq $($xs:tt)* ) => { 354 | impl ::core::cmp::Eq for $struct_name {} 355 | $crate::__impl_traits!($struct_name, $($xs)*); 356 | }; 357 | ( $struct_name:ident, PartialOrd $($xs:tt)* ) => { 358 | impl ::core::cmp::PartialOrd for $struct_name { 359 | fn partial_cmp(&self, other: &Self) -> ::core::option::Option<::core::cmp::Ordering> { 360 | ::core::cmp::PartialOrd::partial_cmp($struct_name::as_ref(self), $struct_name::as_ref(other)) 361 | } 362 | 363 | fn lt(&self, other: &Self) -> bool { 364 | ::core::cmp::PartialOrd::lt($struct_name::as_ref(self), $struct_name::as_ref(other)) 365 | } 366 | 367 | fn le(&self, other: &Self) -> bool { 368 | ::core::cmp::PartialOrd::le($struct_name::as_ref(self), $struct_name::as_ref(other)) 369 | } 370 | 371 | fn gt(&self, other: &Self) -> bool { 372 | ::core::cmp::PartialOrd::gt($struct_name::as_ref(self), $struct_name::as_ref(other)) 373 | } 374 | 375 | fn ge(&self, other: &Self) -> bool { 376 | ::core::cmp::PartialOrd::ge($struct_name::as_ref(self), $struct_name::as_ref(other)) 377 | } 378 | } 379 | $crate::__impl_traits!($struct_name, $($xs)*); 380 | }; 381 | ( $struct_name:ident, Ord $($xs:tt)* ) => { 382 | impl ::core::cmp::Ord for $struct_name { 383 | fn cmp(&self, other: &Self) -> ::core::cmp::Ordering { 384 | ::core::cmp::Ord::cmp($struct_name::as_ref(self), $struct_name::as_ref(other)) 385 | } 386 | } 387 | $crate::__impl_traits!($struct_name, $($xs)*); 388 | }; 389 | ( $struct_name:ident, Clone $($xs:tt)* ) => { 390 | impl ::core::clone::Clone for $struct_name { 391 | fn clone(&self) -> Self { 392 | let cloned = ::core::clone::Clone::clone($struct_name::as_ref(self)); 393 | Self { 394 | __private_inner: unsafe { 395 | ::core::mem::transmute::<_, $crate::__private_mod::ErasedStorage<{ $struct_name::__SIZE }, { $struct_name::__ALIGN }>>(cloned) 396 | } 397 | } 398 | } 399 | } 400 | $crate::__impl_traits!($struct_name, $($xs)* ); 401 | }; 402 | ( $struct_name:ident, Copy $($xs:tt)* ) => { 403 | const _: fn() = || { 404 | fn assert_trait(_: T) {} 405 | assert_trait(__stacklover_inner_unreachable()); 406 | }; 407 | impl ::core::marker::Copy for $struct_name {} 408 | $crate::__impl_traits!($struct_name, $($xs)* # NO_DROP ); 409 | }; 410 | ( $struct_name:ident, Hash $($xs:tt)* ) => { 411 | impl ::core::hash::Hash for $struct_name { 412 | fn hash(&self, state: &mut H) { 413 | ::core::hash::Hash::hash($struct_name::as_ref(self), state) 414 | } 415 | } 416 | $crate::__impl_traits!($struct_name, $($xs)*); 417 | }; 418 | ( $struct_name:ident, Debug $($xs:tt)* ) => { 419 | impl ::core::fmt::Debug for $struct_name { 420 | fn fmt(&self, f: &mut ::core::fmt::Formatter<'_>) -> ::core::fmt::Result { 421 | ::core::fmt::Debug::fmt($struct_name::as_ref(self), f) 422 | } 423 | } 424 | $crate::__impl_traits!($struct_name, $($xs)*); 425 | }; 426 | } 427 | 428 | pub mod __private_mod { 429 | pub struct ConstUsize; 430 | 431 | pub trait ToAlignedZst { 432 | // Zero-sized type 433 | type AlignedZst; 434 | } 435 | 436 | macro_rules! define_aligned_zsts { 437 | ($($n:literal $zst_name:ident),* ) => { 438 | $( 439 | #[repr(align($n))] 440 | #[derive(Clone, Copy)] 441 | pub struct $zst_name; 442 | impl ToAlignedZst for ConstUsize<$n> { 443 | type AlignedZst = $zst_name; 444 | } 445 | )* 446 | }; 447 | } 448 | 449 | // 1 up to 2^29 450 | // https://doc.rust-lang.org/reference/type-layout.html#the-alignment-modifiers 451 | define_aligned_zsts! {1 Zst1, 2 Zst2, 4 Zst4, 8 Zst8, 16 Zst16, 32 Zst32, 64 Zst64, 128 Zst128, 256 Zst256, 512 Zst512, 1024 Zst1024, 2048 Zst2048, 4096 Zst4096, 8192 Zst8192, 16384 Zst16384, 32768 Zst32768, 65536 Zst65536, 131072 Zst131072, 262144 Zst262144, 524288 Zst524288, 1048576 Zst1048576, 2097152 Zst2097152, 4194304 Zst4194304, 8388608 Zst8388608, 16777216 Zst16777216, 33554432 Zst33554432, 67108864 Zst67108864, 134217728 Zst134217728, 268435456 Zst268435456, 536870912 Zst536870912} 452 | 453 | // The Copy impl here is only supposed to allow the impl of Copy for the wrapper struct. Do NOT use it to duplicate the contained value without caution. 454 | #[derive(Clone, Copy)] 455 | pub union ErasedStorage 456 | where 457 | ConstUsize: ToAlignedZst, 458 | as ToAlignedZst>::AlignedZst: Copy, 459 | { 460 | _array: ::core::mem::MaybeUninit<[u8; SIZE]>, 461 | _zero: ::core::mem::ManuallyDrop< as ToAlignedZst>::AlignedZst>, 462 | #[allow(clippy::type_complexity)] 463 | _phantom: ::core::marker::PhantomData<( 464 | // for !Send + !Sync by default 465 | *const (), 466 | // for !Unpin by default 467 | ::core::marker::PhantomPinned, 468 | // for !UnwindSafe by default 469 | ::core::marker::PhantomData<&'static mut ()>, 470 | // for !Sync + !RefUnwindSafe by default 471 | ::core::marker::PhantomData<::core::cell::UnsafeCell<()>>, 472 | )>, 473 | } 474 | } 475 | -------------------------------------------------------------------------------- /tests/lib.rs: -------------------------------------------------------------------------------- 1 | use futures::{SinkExt as _, StreamExt as _}; 2 | use std::cell::Cell; 3 | use std::fmt::Debug; 4 | use std::future::Future; 5 | use std::hash::Hash; 6 | use std::mem::{align_of, align_of_val, size_of}; 7 | use std::panic::{RefUnwindSafe, UnwindSafe}; 8 | use std::pin::Pin; 9 | use std::rc::Rc; 10 | use std::sync::{Arc, Mutex}; 11 | use std::task::{Context, Poll}; 12 | 13 | #[test] 14 | fn it_works() { 15 | stacklover::define_struct! { 16 | // struct name to be defined 17 | Iterator1, 18 | fn (dep1: &str, dep2: i32) -> impl Iterator { 19 | create(dep1, dep2) 20 | }, 21 | impls = (Send, Sync), 22 | } 23 | fn create(dep1: &str, dep2: i32) -> impl Iterator { 24 | vec![1, 2, 3, dep1.len() as i32, dep2] 25 | .into_iter() 26 | .map(|x| x * 2) 27 | } 28 | assert_eq!(size_of::(), size_of_val(&create("", 0))); 29 | assert_eq!(align_of::(), align_of_val(&create("", 0))); 30 | 31 | let (tx, rx) = std::sync::mpsc::channel(); 32 | std::thread::spawn(move || { 33 | let iter = Iterator1::new("hello", 100); 34 | tx.send(iter).unwrap(); 35 | }); 36 | 37 | let mut iter = rx.recv().unwrap(); 38 | assert_eq!(iter.as_ref().size_hint(), (5, Some(5))); 39 | assert_eq!(iter.as_mut().next(), Some(2)); 40 | assert_eq!( 41 | iter.into_inner().into_iter().collect::>(), 42 | vec![4, 6, 10, 200] 43 | ); 44 | } 45 | 46 | #[test] 47 | fn it_works_with_deriving() { 48 | stacklover::define_struct! { 49 | Tuple1, 50 | fn (dep1: &str, dep2: i32) -> (String, i32, bool) { 51 | create(dep1, dep2) 52 | }, 53 | impls = ( PartialEq, Eq, Clone, Debug ), 54 | } 55 | fn create(dep1: &str, dep2: i32) -> (String, i32, bool) { 56 | (dep1.to_owned(), dep2, false) 57 | } 58 | 59 | let x: Tuple1 = Tuple1::new("hello", 100); 60 | let bare = create("hello", 100); 61 | assert_eq!(format!("{:?}", x), format!("{:?}", bare)); 62 | assert_eq!(x, x); 63 | assert_eq!(x.clone().into_inner(), x.into_inner()); 64 | } 65 | 66 | #[test] 67 | fn it_works_with_copy() { 68 | stacklover::define_struct! { 69 | SimpleType, 70 | fn (a: i32) -> i32 { 71 | a 72 | }, 73 | impls = ( Clone, Copy, Debug ), 74 | } 75 | let x: SimpleType = SimpleType::new(42); 76 | assert_eq!(x.into_inner(), 42); 77 | assert_eq!(x.into_inner(), 42); 78 | } 79 | 80 | #[test] 81 | fn it_works_with_non_trivial_drop() { 82 | let flag = Rc::new(Cell::new(false)); 83 | struct HasDrop { 84 | flag: Rc>, 85 | } 86 | impl Drop for HasDrop { 87 | fn drop(&mut self) { 88 | self.flag.set(true); 89 | } 90 | } 91 | stacklover::define_struct! { 92 | DroppingType, 93 | fn (flag: Rc>) -> HasDrop { 94 | HasDrop { flag } 95 | }, 96 | impls = ( ), 97 | } 98 | let x = DroppingType::new(flag.clone()); 99 | drop(x); 100 | assert!(flag.get(), "flag should have been set in drop impl"); 101 | } 102 | 103 | #[test] 104 | fn it_works_with_deriving_all() { 105 | stacklover::define_struct! { 106 | I32, 107 | fn (dep2: i32) -> i32 { 108 | dep2 109 | }, 110 | impls = ( Send, Sync, Unpin, UnwindSafe, RefUnwindSafe, PartialEq, Eq, PartialOrd, Ord, Clone, Hash, Debug, ), 111 | } 112 | fn assert_traits< 113 | T: Send 114 | + Sync 115 | + Unpin 116 | + UnwindSafe 117 | + RefUnwindSafe 118 | + PartialEq 119 | + Eq 120 | + PartialOrd 121 | + Ord 122 | + Clone 123 | + Hash 124 | + Debug, 125 | >( 126 | _: &T, 127 | ) { 128 | } 129 | 130 | let x: I32 = I32::new(100); 131 | let bare = 100; 132 | assert_traits(&x); 133 | assert_eq!(format!("{:?}", x), format!("{:?}", bare)); 134 | assert_eq!(x, x); 135 | assert_eq!(x.clone().into_inner(), x.into_inner()); 136 | } 137 | 138 | #[test] 139 | fn it_works_with_wrap_params() { 140 | stacklover::define_struct! { 141 | Iterator1, 142 | fn (dep1: &str, dep2: i32) -> Result, std::io::Error> { 143 | let iter = create(dep1, dep2); 144 | Ok(iter) 145 | }, 146 | impls = (Send), 147 | inner_type = impl Iterator, 148 | wrapped_type = Result<__Inner__, std::io::Error>, 149 | to_wrapped_struct = |result, inner_to_struct| { result.map(inner_to_struct) }, 150 | } 151 | fn create(dep1: &str, dep2: i32) -> impl Iterator { 152 | vec![1, 2, 3, dep1.len() as i32, dep2] 153 | .into_iter() 154 | .map(|x| x * 2) 155 | } 156 | assert_eq!(size_of::(), size_of_val(&create("", 0))); 157 | assert_eq!(align_of::(), align_of_val(&create("", 0))); 158 | 159 | let (tx, rx) = std::sync::mpsc::channel(); 160 | std::thread::spawn(move || { 161 | let result: Result = Iterator1::new("hello", 100); 162 | if let Ok(iter) = result { 163 | tx.send(iter).unwrap(); 164 | } else { 165 | assert!(false); 166 | } 167 | }); 168 | 169 | let mut iter = rx.recv().unwrap(); 170 | assert_eq!(iter.as_ref().size_hint(), (5, Some(5))); 171 | assert_eq!(iter.as_mut().next(), Some(2)); 172 | assert_eq!( 173 | iter.into_inner().into_iter().collect::>(), 174 | vec![4, 6, 10, 200] 175 | ); 176 | } 177 | 178 | #[test] 179 | fn it_works_with_deriving_traits_and_wrap_params() { 180 | stacklover::define_struct! { 181 | Tuple1, 182 | fn (dep1: &str, dep2: i32) -> Result { 183 | Ok(create(dep1, dep2)) 184 | }, 185 | impls = ( PartialEq, Eq, Debug ), 186 | inner_type = impl PartialEq + Eq + Debug, 187 | wrapped_type = Result<__Inner__, std::io::Error>, 188 | to_wrapped_struct = |result, inner_to_struct| { result.map(inner_to_struct) }, 189 | } 190 | fn create(dep1: &str, dep2: i32) -> impl PartialEq + Eq + Debug { 191 | (dep1.to_owned(), dep2, false) 192 | } 193 | 194 | let result: Result = Tuple1::new("hello", 100); 195 | let x: Tuple1 = result.unwrap(); 196 | let bare = create("hello", 100); 197 | assert_eq!(format!("{:?}", x), format!("{:?}", bare)); 198 | assert_eq!(x, x); 199 | } 200 | 201 | #[test] 202 | fn it_works_with_fn() { 203 | stacklover::define_struct! { 204 | MyFn, 205 | fn (s: String) -> impl Fn(i32) -> i32 { 206 | move |i: i32| { i + s.len() as i32 } 207 | }, 208 | impls = (Send), 209 | } 210 | let (tx, rx) = std::sync::mpsc::channel(); 211 | std::thread::spawn(move || { 212 | let fn1 = MyFn::new("hello".to_owned()); 213 | tx.send(fn1).unwrap(); 214 | }); 215 | 216 | let fn1 = rx.recv().unwrap().into_inner(); 217 | assert_eq!(fn1(10), 15); 218 | assert_eq!(fn1(100), 105); 219 | } 220 | 221 | #[tokio::test] 222 | async fn it_works_without_dependency() { 223 | stacklover::define_struct! { 224 | // struct name to be defined 225 | Iterator1, 226 | // empty parameters 227 | fn () -> impl Iterator { 228 | vec![1, 2, 3] 229 | .into_iter() 230 | .map(|x| x * 2) 231 | }, 232 | impls = (Send, Sync), 233 | } 234 | 235 | stacklover::define_struct! { 236 | // struct name to be defined 237 | Iterator2, 238 | // empty parameters 239 | async fn () -> impl Iterator { 240 | vec![1, 2, 3] 241 | .into_iter() 242 | .map(|x| x * 2) 243 | }, 244 | impls = (Send, Sync), 245 | } 246 | 247 | { 248 | let (tx, rx) = std::sync::mpsc::channel(); 249 | std::thread::spawn(move || { 250 | let iter = Iterator1::new(); 251 | tx.send(iter).unwrap(); 252 | }); 253 | 254 | let iter = rx.recv().unwrap().into_inner(); 255 | assert_eq!(iter.into_iter().collect::>(), vec![2, 4, 6]); 256 | }; 257 | 258 | { 259 | let (mut tx, mut rx) = futures::channel::mpsc::channel(1); 260 | 261 | tokio::spawn(async move { 262 | let iter = Iterator2::new().await; 263 | tx.send(iter).await.unwrap(); 264 | }); 265 | 266 | let iter = rx.next().await.unwrap().into_inner(); 267 | assert_eq!(iter.into_iter().collect::>(), vec![2, 4, 6]); 268 | } 269 | } 270 | 271 | #[test] 272 | fn drops() { 273 | let dropped_count = Arc::new(Mutex::new(0)); 274 | struct MyStruct { 275 | dropped: Arc>, 276 | } 277 | impl Drop for MyStruct { 278 | fn drop(&mut self) { 279 | *self.dropped.lock().unwrap() += 1; 280 | } 281 | } 282 | 283 | stacklover::define_struct! { 284 | MyStructStruct, 285 | fn (dropped: Arc>) -> MyStruct { 286 | MyStruct{dropped} 287 | }, 288 | impls = (), 289 | } 290 | 291 | { 292 | let s = MyStructStruct::new(dropped_count.clone()); 293 | assert_eq!(*dropped_count.lock().unwrap(), 0); 294 | let _ = s; 295 | } 296 | assert_eq!(*dropped_count.lock().unwrap(), 1); 297 | { 298 | let _ = MyStructStruct::new(dropped_count.clone()); 299 | assert_eq!(*dropped_count.lock().unwrap(), 2); 300 | } 301 | assert_eq!(*dropped_count.lock().unwrap(), 2); 302 | { 303 | let s = MyStructStruct::new(dropped_count.clone()); 304 | { 305 | assert_eq!(*dropped_count.lock().unwrap(), 2); 306 | let _inner = s.into_inner(); 307 | assert_eq!(*dropped_count.lock().unwrap(), 2); 308 | } 309 | assert_eq!(*dropped_count.lock().unwrap(), 3); 310 | } 311 | } 312 | 313 | #[tokio::test] 314 | async fn it_works_with_arc() { 315 | // Using Arc caused the error below in some implementation 316 | // error[E0080]: evaluation of constant value failed 317 | // using uninitialized data, but this operation requires initialized memory 318 | stacklover::define_struct! { 319 | MyArc1, 320 | fn (dep1: &str) -> Arc { 321 | Arc::new(dep1.to_owned()) 322 | }, 323 | impls = (), 324 | } 325 | stacklover::define_struct! { 326 | MyArc2, 327 | async fn (dep1: &str) -> Arc { 328 | Arc::new(dep1.to_owned()) 329 | }, 330 | impls = (), 331 | } 332 | } 333 | 334 | #[test] 335 | fn it_works_with_auto_enum_attribute() { 336 | stacklover::define_struct! { 337 | AutoEnumIterator, 338 | #[auto_enums::auto_enum(Iterator)] 339 | fn (x: i32) -> impl Iterator { 340 | match x { 341 | 0 => 1..10, 342 | _ => vec![5, 10].into_iter(), 343 | } 344 | }, 345 | impls = (Send, Sync), 346 | } 347 | 348 | let (tx, rx) = std::sync::mpsc::channel(); 349 | std::thread::spawn(move || { 350 | let iter1 = AutoEnumIterator::new(0); 351 | tx.send(iter1).unwrap(); 352 | let iter2 = AutoEnumIterator::new(1); 353 | tx.send(iter2).unwrap(); 354 | }); 355 | 356 | let iter1 = rx.recv().unwrap().into_inner(); 357 | assert_eq!( 358 | iter1.into_iter().collect::>(), 359 | vec![1, 2, 3, 4, 5, 6, 7, 8, 9] 360 | ); 361 | let iter2 = rx.recv().unwrap().into_inner(); 362 | assert_eq!(iter2.into_iter().collect::>(), vec![5, 10]); 363 | } 364 | 365 | #[tokio::test] 366 | async fn it_works_with_async() { 367 | stacklover::define_struct! { 368 | Iterator2, 369 | async fn (dep1: &'static str, dep2: i32) -> impl Iterator { 370 | tokio::time::sleep(tokio::time::Duration::from_nanos(0)).await; 371 | create(dep1, dep2).await 372 | }, 373 | impls = (Send), 374 | } 375 | async fn create(dep1: &'static str, dep2: i32) -> impl Iterator { 376 | vec![1, 2, 3, dep1.len() as i32, dep2] 377 | .into_iter() 378 | .map(|x| x * 2) 379 | } 380 | assert_eq!(size_of::(), size_of_val(&create("", 0).await)); 381 | assert_eq!(align_of::(), align_of_val(&create("", 0).await)); 382 | 383 | let (mut tx, mut rx) = futures::channel::mpsc::channel(1); 384 | 385 | tokio::spawn(async move { 386 | let iter = Iterator2::new("hello", 100).await; 387 | tx.send(iter).await.unwrap(); 388 | }); 389 | 390 | let mut iter = rx.next().await.unwrap(); 391 | assert_eq!(iter.as_ref().size_hint(), (5, Some(5))); 392 | assert_eq!(iter.as_mut().next(), Some(2)); 393 | assert_eq!( 394 | iter.into_inner().into_iter().collect::>(), 395 | vec![4, 6, 10, 200] 396 | ); 397 | } 398 | 399 | #[tokio::test] 400 | async fn it_works_with_async_and_deriving() { 401 | stacklover::define_struct! { 402 | Tuple1, 403 | async fn (dep1: &str, dep2: i32) -> impl PartialEq + Eq + Debug { 404 | create(dep1, dep2) 405 | }, 406 | impls = ( PartialEq, Eq, Debug ), 407 | } 408 | fn create(dep1: &str, dep2: i32) -> impl PartialEq + Eq + Debug { 409 | (dep1.to_owned(), dep2, false) 410 | } 411 | 412 | let x: Tuple1 = Tuple1::new("hello", 100).await; 413 | let bare = create("hello", 100); 414 | assert_eq!(format!("{:?}", x), format!("{:?}", bare)); 415 | assert_eq!(x, x); 416 | } 417 | 418 | #[tokio::test] 419 | async fn it_works_with_async_fn_and_wrap_params() { 420 | stacklover::define_struct! { 421 | Iterator1, 422 | async fn (dep1: &'static str, dep2: i32) -> Result, std::io::Error> { 423 | tokio::time::sleep(tokio::time::Duration::from_nanos(0)).await; 424 | let iter = create(dep1, dep2).await; 425 | Ok(iter) 426 | }, 427 | impls = (Send), 428 | inner_type = impl Iterator, 429 | wrapped_type = Result<__Inner__, std::io::Error>, 430 | to_wrapped_struct = |result, inner_to_struct| { result.map(inner_to_struct) }, 431 | } 432 | async fn create(dep1: &'static str, dep2: i32) -> impl Iterator { 433 | vec![1, 2, 3, dep1.len() as i32, dep2] 434 | .into_iter() 435 | .map(|x| x * 2) 436 | } 437 | assert_eq!(size_of::(), size_of_val(&create("", 0).await)); 438 | assert_eq!(align_of::(), align_of_val(&create("", 0).await)); 439 | 440 | let (mut tx, mut rx) = futures::channel::mpsc::channel(1); 441 | 442 | tokio::spawn(async move { 443 | let result: Result = Iterator1::new("hello", 100).await; 444 | if let Ok(iter) = result { 445 | tx.send(iter).await.unwrap(); 446 | } else { 447 | assert!(false); 448 | } 449 | }); 450 | 451 | let mut iter = rx.next().await.unwrap(); 452 | assert_eq!(iter.as_ref().size_hint(), (5, Some(5))); 453 | assert_eq!(iter.as_mut().next(), Some(2)); 454 | assert_eq!( 455 | iter.into_inner().into_iter().collect::>(), 456 | vec![4, 6, 10, 200] 457 | ); 458 | } 459 | 460 | #[tokio::test] 461 | async fn it_works_with_async_and_deriving_traits_and_wrap_params() { 462 | stacklover::define_struct! { 463 | Tuple1, 464 | async fn (dep1: &str, dep2: i32) -> Result { 465 | Ok(create(dep1, dep2)) 466 | }, 467 | impls = ( PartialEq, Eq, Debug ), 468 | inner_type = impl PartialEq + Eq + Debug, 469 | wrapped_type = Result<__Inner__, std::io::Error>, 470 | to_wrapped_struct = |result, inner_to_struct| { result.map(inner_to_struct) }, 471 | } 472 | fn create(dep1: &str, dep2: i32) -> impl PartialEq + Eq + Debug { 473 | (dep1.to_owned(), dep2, false) 474 | } 475 | 476 | let result: Result = Tuple1::new("hello", 100).await; 477 | let x: Tuple1 = result.unwrap(); 478 | let bare = create("hello", 100); 479 | assert_eq!(format!("{:?}", x), format!("{:?}", bare)); 480 | assert_eq!(x, x); 481 | } 482 | 483 | #[tokio::test] 484 | async fn it_works_with_as_pin_mut() { 485 | stacklover::define_struct! { 486 | Future1, 487 | #[allow(clippy::manual_async_fn)] 488 | fn () -> impl Future { 489 | async { 490 | tokio::time::sleep(tokio::time::Duration::from_nanos(1)).await; 491 | 10 492 | } 493 | }, 494 | impls = (), // NOTE: !Unpin 495 | } 496 | 497 | impl Future for Future1 { 498 | type Output = i32; 499 | 500 | fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll { 501 | self.as_pin_mut().poll(cx) 502 | } 503 | } 504 | 505 | assert_eq!(Future1::new().await, 10); 506 | } 507 | 508 | #[test] 509 | fn it_works_with_as_pin_drop() { 510 | struct Trap { 511 | data: String, 512 | ptr: *const String, 513 | _marker: std::marker::PhantomPinned, 514 | } 515 | impl Drop for Trap { 516 | fn drop(&mut self) { 517 | let this = unsafe { Pin::new_unchecked(self) }; 518 | if !this.ptr.is_null() { 519 | // If ptr is initialized, we know that self has been pinned and hasn't moved. Assert that this is still the case. 520 | assert_eq!(this.ptr, &this.data, "ptr should point to our own data"); 521 | // Simulate a read if the ptr was initialized to trap miri if the pointer provenance was somehow disabled. 522 | let _ = unsafe { &*this.ptr }.to_string(); 523 | } 524 | } 525 | } 526 | trait Init { 527 | fn init(self: Pin<&mut Self>); 528 | } 529 | impl Init for Trap { 530 | fn init(self: Pin<&mut Self>) { 531 | let self_ptr = &self.data as *const String; 532 | let this = unsafe { self.get_unchecked_mut() }; 533 | this.ptr = self_ptr; 534 | } 535 | } 536 | fn create() -> Trap { 537 | Trap { 538 | data: "foobar".to_string(), 539 | ptr: core::ptr::null(), 540 | _marker: std::marker::PhantomPinned, 541 | } 542 | } 543 | stacklover::define_struct! { 544 | PinUpType, 545 | fn () -> impl Init { 546 | create() 547 | }, 548 | impls = (), // NOTE: !Unpin 549 | } 550 | let pinned = PinUpType::new(); 551 | // TODO: replace with ::core::pin::pin! when the MSRV is bumped to >1.68.0 552 | futures::pin_mut!(pinned); 553 | pinned.as_pin_mut().init(); 554 | // ... pinned is dropped here, which activates the trap in miri. 555 | } 556 | 557 | #[tokio::test] 558 | async fn it_works_with_async_auto_enum_attribute() { 559 | stacklover::define_struct! { 560 | AutoEnumIterator, 561 | #[auto_enums::auto_enum(Iterator)] 562 | async fn (x: i32) -> impl Iterator { 563 | match x { 564 | 0 => 1..10, 565 | _ => vec![5, 10].into_iter(), 566 | } 567 | }, 568 | impls = (Send), 569 | } 570 | 571 | let (mut tx, mut rx) = futures::channel::mpsc::channel(1); 572 | tokio::spawn(async move { 573 | let iter1 = AutoEnumIterator::new(0).await; 574 | tx.send(iter1).await.unwrap(); 575 | let iter2 = AutoEnumIterator::new(1).await; 576 | tx.send(iter2).await.unwrap(); 577 | }); 578 | 579 | let iter1 = rx.next().await.unwrap().into_inner(); 580 | assert_eq!( 581 | iter1.into_iter().collect::>(), 582 | vec![1, 2, 3, 4, 5, 6, 7, 8, 9] 583 | ); 584 | let iter2 = rx.next().await.unwrap().into_inner(); 585 | assert_eq!(iter2.into_iter().collect::>(), vec![5, 10]); 586 | } 587 | 588 | const fn size_of_val(_: &T) -> usize { 589 | size_of::() 590 | } 591 | -------------------------------------------------------------------------------- /tests/lib_no_std.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | 3 | use core::mem::size_of; 4 | 5 | #[test] 6 | fn it_works_with_no_std() { 7 | stacklover::define_struct! { 8 | Iterator1, 9 | fn (dep1: &str, dep2: i32) -> impl Iterator { 10 | create(dep1, dep2) 11 | }, 12 | impls = (Send, Sync), 13 | } 14 | fn create(dep1: &str, dep2: i32) -> impl Iterator { 15 | [1, 2, 3, dep1.len() as i32, dep2] 16 | .into_iter() 17 | .map(|x| x * 2) 18 | } 19 | assert_eq!(size_of::(), size_of_val(&create("", 0))); 20 | let mut iter: Iterator1 = Iterator1::new("hello", 100); 21 | assert_eq!(iter.as_ref().size_hint(), (5, Some(5))); 22 | assert_eq!(iter.as_mut().next(), Some(2)); 23 | let mut inner = iter.into_inner(); 24 | assert_eq!(inner.next(), Some(4)); 25 | assert_eq!(inner.next(), Some(6)); 26 | assert_eq!(inner.next(), Some(10)); 27 | assert_eq!(inner.next(), Some(200)); 28 | assert_eq!(inner.next(), None); 29 | } 30 | 31 | const fn size_of_val(_: &T) -> usize { 32 | size_of::() 33 | } 34 | --------------------------------------------------------------------------------