{"id":15647352,"url":"https://github.com/nwtgck/stacklover-rust","last_synced_at":"2025-10-06T14:46:39.574Z","repository":{"id":219602663,"uuid":"745545686","full_name":"nwtgck/stacklover-rust","owner":"nwtgck","description":"Zero-cost type for stack without complicated type or Box","archived":false,"fork":false,"pushed_at":"2025-03-13T23:41:19.000Z","size":117,"stargazers_count":45,"open_issues_count":1,"forks_count":2,"subscribers_count":2,"default_branch":"develop","last_synced_at":"2025-03-27T08:48:43.832Z","etag":null,"topics":["no-std","rust","stable-rust"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nwtgck.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-01-19T15:13:53.000Z","updated_at":"2025-02-20T14:16:49.000Z","dependencies_parsed_at":"2024-03-30T03:27:04.148Z","dependency_job_id":"8513104c-ce2b-4eee-8ebe-1dcd362e7a44","html_url":"https://github.com/nwtgck/stacklover-rust","commit_stats":{"total_commits":94,"total_committers":3,"mean_commits":"31.333333333333332","dds":"0.12765957446808507","last_synced_commit":"8ec7722c4ce3655d51f38318ab003205b3ef704e"},"previous_names":["nwtgck/stacklover-rust"],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nwtgck%2Fstacklover-rust","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nwtgck%2Fstacklover-rust/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nwtgck%2Fstacklover-rust/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nwtgck%2Fstacklover-rust/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nwtgck","download_url":"https://codeload.github.com/nwtgck/stacklover-rust/tar.gz/refs/heads/develop","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246178109,"owners_count":20736085,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["no-std","rust","stable-rust"],"created_at":"2024-10-03T12:18:49.187Z","updated_at":"2025-10-06T14:46:34.552Z","avatar_url":"https://github.com/nwtgck.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# stacklover\n[![CI](https://github.com/nwtgck/stacklover-rust/actions/workflows/ci.yml/badge.svg)](https://github.com/nwtgck/stacklover-rust/actions/workflows/ci.yml)\n\nZero-cost type for stack without complicated type or Box\n\n## Why?\n\nRust 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.\n\n```rust\nuse std::convert::identity;\nuse std::sync::{Arc, Mutex};\n\n// Super long and complicated type!!\ntype IteratorI32 = core::iter::Chain\u003c\n    core::iter::TakeWhile\u003c\n        core::iter::Map\u003ccore::ops::RangeFrom\u003ci32\u003e, fn(i32) -\u003e i32\u003e,\n        fn(\u0026i32) -\u003e bool,\n    \u003e,\n    core::iter::FlatMap\u003c\n        core::iter::Chain\u003c\n            core::iter::Map\u003ccore::str::Chars\u003c'static\u003e, fn(char) -\u003e i32\u003e,\n            core::array::IntoIter\u003ci32, 1\u003e,\n        \u003e,\n        [i32; 2],\n        fn(i32) -\u003e [i32; 2],\n    \u003e,\n\u003e;\n// (Here is the end of type defintion!)\n\nstruct MyService {\n    iter: Mutex\u003cOption\u003cIteratorI32\u003e\u003e,\n}\n\nimpl MyService {\n    fn put_iter(\u0026self, message: \u0026str) {\n        // Create an iterator\n        let iter: IteratorI32 = (1..)\n            .map(identity::\u003cfn(i32) -\u003e i32\u003e(|x| x * 3))             // NOTE: An extra identity function needed. Without this, an error \"expected fn pointer, found closure\" occurrs.\n            .take_while(identity::\u003cfn(\u0026i32) -\u003e bool\u003e(|x| *x \u003c 10))  // NOTE: An extra identity function needed.\n            .chain(\n                \"HELLO\"\n                    .chars()\n                    .map(identity::\u003cfn(char) -\u003e i32\u003e(|c| c as i32))              // NOTE: An extra identity function needed.\n                    .chain([message.len() as i32])\n                    .flat_map(identity::\u003cfn(i32) -\u003e [i32; 2]\u003e(|i| [i, i - 65])), // NOTE: An extra identity function needed.\n            );\n        // Put the iterator\n        self.iter.lock().unwrap().replace(iter);\n    }\n    \n    // ...\n}\n```\n\nA simple solution is to use `Box` as shown below and allocate the iterator on the heap. \n\n```rust\nuse std::sync::{Arc, Mutex};\n\ntype IteratorI32 = Box\u003cdyn Iterator\u003cItem = i32\u003e + Send + 'static\u003e;\n\nstruct MyService {\n    iter: Mutex\u003cOption\u003cIteratorI32\u003e\u003e,\n}\n\nimpl MyService {\n    fn put_iter(\u0026self, message: \u0026str) {\n        // Create an iterator\n        let iter: IteratorI32 = Box::new(\n            (1..).map(|x| x * 3).take_while(|x| *x \u003c 10).chain( // no extra identity function needed at all\n                \"HELLO\"\n                    .chars()\n                    .map(|c| c as i32)\n                    .chain([message.len() as i32])\n                    .flat_map(|i| [i, i - 65]),\n            ),\n        );\n        // Put the iterator\n        self.iter.lock().unwrap().replace(iter);\n    }\n    \n    // ...\n}\n```\n\nHowever, we sometimes avoid unnecessary heap allocations and prefer stack allocations. Stack allocation may allow us to inlining functions or static dispatches and improve performance.\n\nIn 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.\n\n```rust\nuse std::sync::{Arc, Mutex};\n\nstacklover::define_struct! {\n    IteratorI32,\n    fn (message: \u0026str) -\u003e impl Iterator\u003cItem = i32\u003e {\n        (1..).map(|x| x * 3).take_while(|x| *x \u003c 10).chain(\n            \"HELLO\"\n                .chars()\n                .map(|c| c as i32)\n                .chain([message.len() as i32])\n                .flat_map(|i| [i, i - 65]),\n        )\n    },\n    impls = (Send, Sync),\n}\n\nstruct MyService {\n    iter: Mutex\u003cOption\u003cIteratorI32\u003e\u003e,\n}\n\nimpl MyService {\n    fn put_iter(\u0026self, message: \u0026str) {\n        // Create an iterator\n        let iter: IteratorI32 = IteratorI32::new(message);\n        // Put the iterator\n        self.iter.lock().unwrap().replace(iter);\n    }\n    \n    // ...\n}\n```\n\nstacklover also works with no_std.\n\n## Performance\n\nPerformance with `stacklover` is the same as with bare type.\n\n* bare: `impl Iterator\u003cItem = i64\u003e`\n* boxed: `Box\u003cimpl Iterator\u003cItem = i64\u003e\u003e`\n* stacklover\n\nHere is a result of [iter_benchmark](bench/benches/iter_benchmark.rs) in [GitHub Actions](https://github.com/nwtgck/stacklover-rust/actions/workflows/ci.yml).\n\n```txt\niterator sum/bare       time:   [12.329 ns 12.487 ns 12.670 ns]\niterator sum/boxed      time:   [348.60 ms 352.41 ms 356.41 ms]\niterator sum/stacklover time:   [11.494 ns 11.657 ns 11.819 ns]\n```\n\nNote that the time unit of \"boxed\" is ms not ns. Investigation is underway to know why Box is too slow.\n\nData size of struct created by `stacklover` is the same as bare type:\nhttps://github.com/nwtgck/stacklover-rust/blob/796c2dcff68032dbbc064314018565cb21d8c94f/tests/lib.rs#L19\n\n\n## How to use\n\nAdd `stacklover` to the `Cargo.toml`.\n\n###  Dependency\n\n```toml\n[dependencies]\nstacklover = { git = \"https://github.com/nwtgck/stacklover-rust.git\", rev = \"efbe91996e5554a9bf7bf3c35bd67c4ed2770866\" }\n```\n\n### Simple example\nCreate by `YourStruct::new()` and access its inner value by `.as_ref()`, `.as_mut()`, `.into_inner()` and `.as_pin_mut()`. Here is an example.\n\n```rust\nuse std::fmt::Debug;\n\nfn main() {\n    // Use `define_struct!` everywhere `struct YourStruct;` can be used.\n    stacklover::define_struct! {\n        // Struct name to be defined.\n        IteratorI32,\n        // Note that the function below has no name.\n        fn (i: i32) -\u003e impl Iterator\u003cItem = i32\u003e + Debug {\n            (1..i).map(|x| x * 3).take_while(|x| *x \u003c 10)\n        },\n        impls = (Send, Sync, Debug),\n    }\n\n    // Use `IteratorI32` instead of complicated type.\n    let mut x: IteratorI32 = IteratorI32::new(10);\n\n    println!(\"x={:?}\", x);\n    // Output:\n    // x=TakeWhile { iter: Map { iter: 1..10 }, flag: false }\n\n    println!(\"size_hint={:?}\", x.as_ref().size_hint());\n    // Output:\n    // size_hint=(0, Some(9))\n\n    println!(\"next={:?}\", x.as_mut().next());\n    // Output:\n    // next=Some(3)\n\n    // Get `Iterator\u003cItem = i32\u003e` by .into_inner()\n    let iter /* : impl Iterator\u003cItem = i32\u003e */ = x.into_inner();\n    for i in iter {\n        println!(\"i={}\", i)\n    }\n    // Output:\n    // i=6\n    // i=9\n}\n```\n\n### Async function example\nAsync function can be used as well.\n\n```rust\n#[tokio::main]\nasync fn main() {\n    stacklover::define_struct! {\n        IteratorI32,\n        // async function\n        async fn (i: i32) -\u003e impl Iterator\u003cItem = i32\u003e {\n            (1..i).map(|x| x * 3).take_while(|x| *x \u003c 10)\n        },\n        impls = (Send, Sync),\n    }\n\n    let x: IteratorI32 = IteratorI32::new(10).await; // .await used\n    let iter /* : impl Iterator\u003cItem = i32\u003e */ = x.into_inner();\n    for i in iter {\n        println!(\"i={}\", i)\n    }\n    // Output:\n    // i=3\n    // i=6\n    // i=9\n}\n```\n\n### Wrapped type example\nYou can store the wrapped type `T` in `Result\u003cT\u003e`.  Here is an example using a creation function that returns `Result\u003cimpl Iterator\u003c...\u003e\u003e`, but the struct `IteratorI32` stores `impl Iterator\u003c...\u003e`.\n\n```rust\nstacklover::define_struct! {\n    IteratorI32,\n    // The function below returns Result but the inner value should be Iterator, not Result.\n    fn (i: i32) -\u003e Result\u003cimpl Iterator\u003cItem = i32\u003e, std::io::Error\u003e {\n        let iter = (1..i).map(|x| x * 3).take_while(|x| *x \u003c 10);\n        Ok(iter)\n    },\n    // Specify parameters in the following order.\n    impls = (Send, Sync),\n    inner_type = impl Iterator\u003cItem=i32\u003e,\n    wrapped_type = Result\u003c__Inner__, std::io::Error\u003e, // Type paramter `__Inner__` is predefined in the macro.\n    to_wrapped_struct = |result, inner_to_struct| { result.map(inner_to_struct) },\n}\n\n// Use `IteratorI32` instead of complicated type.\nlet result: Result\u003cIteratorI32, std::io::Error\u003e = IteratorI32::new(10);\n// Get IteratorI32 from Result by ?.\nlet x: IteratorI32 = result?;\n```\n\nHere is an example with more nested type and async function.\n\n```rust\nstacklover::define_struct! {\n    IteratorI32,\n    async fn (i: i32) -\u003e Result\u003c(impl Iterator\u003cItem = i32\u003e, String, f32), std::io::Error\u003e {\n        let iter = (1..i).map(|x| x * 3).take_while(|x| *x \u003c 10);\n        Ok((iter, \"hello\".to_owned(), 3.14))\n    },\n    // Specify parameters in the following order.\n    impls = (Send, Sync),\n    inner_type = impl Iterator\u003cItem=i32\u003e,\n    wrapped_type = Result\u003c(__Inner__, String, f32), std::io::Error\u003e, // Type paramter `__Inner__` is predefined in the macro.\n    to_wrapped_struct = |result, inner_to_struct| { result.map(|(iter, s, f)| (inner_to_struct(iter), s, f)) },\n}\n\nlet result: Result\u003c(IteratorI32, String, f32), std::io::Error\u003e = IteratorI32::new(10).await;\n// Get a tuple with IteratorI32 from Result by ?.\nlet (iter, s, f): (IteratorI32, String, f32) = result?;\n```\n\n### Implement trait example\n\nIn `impls = (...)` you can specify the following traits.\n* [Send](https://doc.rust-lang.org/std/marker/trait.Send.html)\n* [Sync](https://doc.rust-lang.org/std/marker/trait.Sync.html)\n* [Unpin](https://doc.rust-lang.org/std/marker/trait.Unpin.html)\n* [UnwindSafe](https://doc.rust-lang.org/std/panic/trait.UnwindSafe.html)\n* [RefUnwindSafe](https://doc.rust-lang.org/std/panic/trait.RefUnwindSafe.html)\n* [PartialEq](https://doc.rust-lang.org/std/cmp/trait.PartialEq.html)\n* [Eq](https://doc.rust-lang.org/std/cmp/trait.Eq.html)\n* [PartialOrd](https://doc.rust-lang.org/std/cmp/trait.PartialOrd.html)\n* [Ord](https://doc.rust-lang.org/std/cmp/trait.Ord.html)\n* [Clone](https://doc.rust-lang.org/std/clone/trait.Clone.html)\n* [Copy](https://doc.rust-lang.org/core/marker/trait.Copy.html)\n* [Hash](https://doc.rust-lang.org/std/hash/trait.Hash.html)\n* [Debug](https://doc.rust-lang.org/std/fmt/trait.Debug.html)\n\nAt compile time, it is asserted whether the inner type implements the trait in `impls = (...)`. \n\n### Using attributes - auto_enums\nAttributes 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.\n\n```rust\nfn main() {\n    stacklover::define_struct! {\n        AutoEnumIterator,\n        #[auto_enums::auto_enum(Iterator)]\n        fn (x: i32) -\u003e impl Iterator\u003cItem=i32\u003e {\n            match x {\n                0 =\u003e 1..10,\n                _ =\u003e vec![5, 10].into_iter(),\n            }\n        },\n        impls = (),\n    }\n\n    let x1: AutoEnumIterator = AutoEnumIterator::new(0);\n    let iter1 /* : impl Iterator\u003cItem = i32\u003e */ = x1.into_inner();\n    println!(\"iter1={:?}\", iter1.collect::\u003cVec\u003c_\u003e\u003e());\n    // Output: iter1=[1, 2, 3, 4, 5, 6, 7, 8, 9]\n\n    let x2: AutoEnumIterator = AutoEnumIterator::new(4);\n    let iter2 /* : impl Iterator\u003cItem = i32\u003e */ = x2.into_inner();\n    println!(\"iter2={:?}\", iter2.collect::\u003cVec\u003c_\u003e\u003e());\n    // Output: iter2=[5, 10]\n}\n```\n\n### Define separately for IDE and formatter\nYou can define the creation function separately. This may allow you to have better IDE and formatter support. \n```rust\nstacklover::define_struct! {\n    IteratorI32,\n    fn (message: \u0026str) -\u003e impl Iterator\u003cItem = i32\u003e {\n        // Call the function\n        create_iter_i32(message)\n    },\n    impls = (),\n}\n\n// Define a creation function separately\nfn create_iter_i32(message: \u0026str) -\u003e impl Iterator\u003cItem = i32\u003e {\n    (1..).map(|x| x * 3).take_while(|x| *x \u003c 10).chain(\n        \"HELLO\"\n            .chars()\n            .map(|c| c as i32)\n            .chain([message.len() as i32])\n            .flat_map(|i| [i, i - 65]),\n    )\n}\n```\n\n## How it works\n\nRead [example1.expanded.rs](expand/src/bin/example1.expanded.rs) expanded from [example1.rs](expand/src/bin/example1.rs).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnwtgck%2Fstacklover-rust","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnwtgck%2Fstacklover-rust","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnwtgck%2Fstacklover-rust/lists"}