{"id":15586450,"url":"https://github.com/alecdotninja/tailcall","last_synced_at":"2025-04-08T02:36:11.711Z","repository":{"id":54110315,"uuid":"225096030","full_name":"alecdotninja/tailcall","owner":"alecdotninja","description":"Safe, zero-cost tail recursion for stable Rust","archived":false,"fork":false,"pushed_at":"2024-10-06T21:53:23.000Z","size":73,"stargazers_count":158,"open_issues_count":7,"forks_count":8,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-01T01:36:41.319Z","etag":null,"topics":["recursion","rust","rust-lang","tail-recursion","tailcall"],"latest_commit_sha":null,"homepage":"https://crates.io/crates/tailcall","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alecdotninja.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE-APACHE","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":"2019-12-01T02:25:00.000Z","updated_at":"2025-03-26T00:34:21.000Z","dependencies_parsed_at":"2024-01-15T21:36:39.714Z","dependency_job_id":"0aa8c01e-1d6f-4175-855f-fb3826bdf26a","html_url":"https://github.com/alecdotninja/tailcall","commit_stats":{"total_commits":28,"total_committers":6,"mean_commits":4.666666666666667,"dds":0.5714285714285714,"last_synced_commit":"823a3d5b9b6fa80bf6e58b28e5da93be494f9316"},"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alecdotninja%2Ftailcall","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alecdotninja%2Ftailcall/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alecdotninja%2Ftailcall/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alecdotninja%2Ftailcall/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alecdotninja","download_url":"https://codeload.github.com/alecdotninja/tailcall/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247765460,"owners_count":20992314,"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":["recursion","rust","rust-lang","tail-recursion","tailcall"],"created_at":"2024-10-02T21:22:38.436Z","updated_at":"2025-04-08T02:36:11.685Z","avatar_url":"https://github.com/alecdotninja.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Tailcall\n\n[![CI](https://github.com/alecdotninja/tailcall/actions/workflows/ci.yml/badge.svg)](https://github.com/alecdotninja/tailcall/actions/workflows/ci.yml)\n[![Current Crates.io Version](https://img.shields.io/crates/v/tailcall.svg)](https://crates.io/crates/tailcall)\n[![Docs](https://docs.rs/tailcall/badge.svg)](https://docs.rs/tailcall)\n\nTailcall is a library that adds safe, zero-cost [tail recursion](https://en.wikipedia.org/wiki/Tail_call) to stable Rust.\n\nEventually, it will be superseded by the [`become` keyword](https://internals.rust-lang.org/t/pre-rfc-explicit-proper-tail-calls/3797/16).\n\n## Installation\n\nTailcall is distributed as a [crate](https://crates.io/crates/tailcall).\n\nAdd this to your `Cargo.toml`:\n\n```toml\n[dependencies]\ntailcall = \"~1\"\n```\n\n## Usage\n\nAdd the `tailcall` attribute to functions which you would like to use tail recursion:\n\n```rust\nuse tailcall::tailcall;\n\n#[tailcall]\nfn gcd(a: u64, b: u64) -\u003e u64 {\n    if b == 0 {\n        a\n    } else {\n        gcd(b, a % b)\n    }\n}\n```\n\nFor more detailed information (including some limitations), please see [the docs](https://docs.rs/tailcall).\n\n## Implementation\n\nThe core idea is to rewrite the function into a loop using the [trampoline approach](https://en.wikipedia.org/wiki/Tail_call#Through_trampolining).\nHere is the (slightly reformatted) expansion for the `gcd` example above:\n\n```rust\nfn gcd(a: u64, b: u64) -\u003e u64 {\n    tailcall::trampoline::run(\n        #[inline(always)] |(a, b)| {\n            tailcall::trampoline::Finish({\n                if b == 0 {\n                    a\n                } else {\n                    return tailcall::trampoline::Recurse((b, a % b))\n                }\n            })\n        },\n        (a, b),\n    )\n}\n```\n\nYou can view the exact expansion for the `tailcall` macro in your use-case with `cargo expand`.\n\n## Development\n\nDevelopment dependencies, testing, documentation generation, packaging, and distribution are all managed via [Cargo](https://doc.rust-lang.org/cargo/getting-started/installation.html).\n\nAfter checking out the repo, run `cargo test` to verify the test suite.\nThe latest documentation can be generated with `cargo doc`.\nBefore commiting, please make sure code is formatted canonically with `cargo fmt` and passes all lints with `cargo clippy`.\nNew versions are released to [crates.io](https://crates.io/crates/tailcall) with `cargo publish`.\n\n## Benchmarks\n\nThere are a few benchmarks available; currently the benchmarks demonstrate that for\nsingle-function tail-recursion, performance is the same as using a loop. Mutual\nrecursion runs, but suffers penalties.\n\n```\n$ cargo bench\n    Finished bench [optimized] target(s) in 0.05s\n     Running target/release/deps/tailcall-b55b2bddb07cb046\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n\n     Running target/release/deps/bench-b8ab29e7ebef8d8d\n\nrunning 4 tests\ntest bench_oddness_boom   ... bench:           6 ns/iter (+/- 0)\ntest bench_oddness_loop   ... bench:           6 ns/iter (+/- 0)\ntest bench_oddness_mutrec ... bench:   4,509,915 ns/iter (+/- 7,095,455)\ntest bench_oddness_rec    ... bench:           3 ns/iter (+/- 0)\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 4 measured; 0 filtered out\n```\n\nIf the optimization level is set to zero so that `bench_oddness_boom` isn't cleverly\noptimized away, it blows the stack as expected:\n\n```\n$ RUSTFLAGS=\"-C opt-level=0\" cargo bench _boom\n    Finished bench [optimized] target(s) in 0.05s\n     Running target/release/deps/tailcall-b55b2bddb07cb046\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n\n     Running target/release/deps/bench-b8ab29e7ebef8d8d\n\nrunning 1 test\n\nthread 'main' has overflowed its stack\nfatal runtime error: stack overflow\n```\n\nIn fact the same occurs when running `RUSTFLAGS=\"-C opt-level=0\" cargo bench _mutrec`\n, indicating mutual recursion can also blow the stack, but the `loop` and tailrec-enabled\nsingle-function, tail-recursive functions enjoy TCO:\n\n```\n$ RUSTFLAGS=\"-C opt-level=0\" cargo bench _loop\n    Finished bench [optimized] target(s) in 0.06s\n     Running target/release/deps/tailcall-b55b2bddb07cb046\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n\n     Running target/release/deps/bench-b8ab29e7ebef8d8d\n\nrunning 1 test\ntest bench_oddness_loop   ... bench:   4,514,730 ns/iter (+/- 7,498,984)\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 3 filtered out\n\n\n$ RUSTFLAGS=\"-C opt-level=0\" cargo bench _rec\n    Finished bench [optimized] target(s) in 0.05s\n     Running target/release/deps/tailcall-b55b2bddb07cb046\n\nrunning 0 tests\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out\n\n     Running target/release/deps/bench-b8ab29e7ebef8d8d\n\nrunning 1 test\ntest bench_oddness_rec    ... bench:  22,416,962 ns/iter (+/- 16,083,896)\n\ntest result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 3 filtered out\n```\n\n\n## Contributing\n\nBug reports and pull requests are welcome on [GitHub](https://github.com/alecdotninja/tailcall).\n\n## License\n\nTailcall is distributed under the terms of both the MIT license and the Apache License (Version 2.0).\n\nSee [LICENSE-APACHE](LICENSE-APACHE) and [LICENSE-MIT](LICENSE-MIT), and [COPYRIGHT](COPYRIGHT) for details.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falecdotninja%2Ftailcall","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falecdotninja%2Ftailcall","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falecdotninja%2Ftailcall/lists"}