{"id":15567004,"url":"https://github.com/ozars/specialized-dispatch","last_synced_at":"2025-10-20T09:27:28.439Z","repository":{"id":232012319,"uuid":"783262657","full_name":"ozars/specialized-dispatch","owner":"ozars","description":"A library for dispatching specialized versions of a function","archived":false,"fork":false,"pushed_at":"2024-04-13T06:08:16.000Z","size":31,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-06T12:52:59.016Z","etag":null,"topics":["function-overloading","procedural-macro","rust","rust-lang","specialization"],"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/ozars.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-04-07T11:59:10.000Z","updated_at":"2024-04-07T23:18:09.000Z","dependencies_parsed_at":"2024-12-09T04:06:53.524Z","dependency_job_id":"8d9f0a04-621b-46ba-90b7-21bca6e23168","html_url":"https://github.com/ozars/specialized-dispatch","commit_stats":{"total_commits":41,"total_committers":1,"mean_commits":41.0,"dds":0.0,"last_synced_commit":"32e5c6c8f1c32ac1903f1895f8f453ac7a62a9ed"},"previous_names":["ozars/specialized-dispatch"],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ozars%2Fspecialized-dispatch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ozars%2Fspecialized-dispatch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ozars%2Fspecialized-dispatch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ozars%2Fspecialized-dispatch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ozars","download_url":"https://codeload.github.com/ozars/specialized-dispatch/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246140591,"owners_count":20729802,"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":["function-overloading","procedural-macro","rust","rust-lang","specialization"],"created_at":"2024-10-02T17:09:33.459Z","updated_at":"2025-10-20T09:27:23.407Z","avatar_url":"https://github.com/ozars.png","language":"Rust","readme":"\u003ch1 align=\"center\"\u003especialized-dispatch\u003c/h1\u003e\n\n\u003cp align=\"center\"\u003e\n  \u003ca href=\"https://github.com/ozars/specialized-dispatch\"\u003e\u003cimg alt=\"Github Repository\" src=\"https://img.shields.io/badge/ozars%2Fspecialized--dispatch-8da0cb?style=for-the-badge\u0026logo=github\u0026label=github\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://crates.io/crates/specialized-dispatch\"\u003e\u003cimg alt=\"crates.io Version\" src=\"https://img.shields.io/crates/v/specialized-dispatch?style=for-the-badge\u0026logo=rust\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://docs.rs/specialized-dispatch/latest/specialized_dispatch/\"\u003e\u003cimg alt=\"docs.rs Documentation\" src=\"https://img.shields.io/docsrs/specialized-dispatch?style=for-the-badge\u0026logo=docs.rs\"\u003e\u003c/a\u003e\n  \u003ca href=\"https://github.com/ozars/specialized-dispatch/actions/workflows/rust.yml\"\u003e\u003cimg alt=\"Github Actions Build\" src=\"https://img.shields.io/github/actions/workflow/status/ozars/specialized-dispatch/rust.yml?style=for-the-badge\u0026logo=github-actions\"\u003e\u003c/a\u003e\n\u003c/p\u003e\n\nThis crate provides a procedural macro, `specialized_dispatch`, a convenient\nway to implement different behaviors based on type of an expression.\n\nThis works by creating different specializations in the callsite by making use\nof [`min_specialization`] nightly feature under the hood.\n\nAs such, the caller needs to enable this nightly feature for the library from\nwhich this macro is called.\n\n[`min_specialization`]: https://doc.rust-lang.org/beta/unstable-book/language-features/min-specialization.html\n\n## Simple Example\n\n```rust\n#![feature(min_specialization)]\n\nuse specialized_dispatch::specialized_dispatch;\n\nfn example\u003cE\u003e(expr: E) -\u003e String {\n    specialized_dispatch!(\n        // Type of the expression -\u003e return type.\n        E -\u003e String,\n        // Defaut implementation.\n        default fn \u003cT\u003e(_: T) =\u003e format!(\"default value\"),\n        // Specialization for concrete type u8.\n        fn (v: u8) =\u003e format!(\"u8: {}\", v),\n        // Specialization for concrete type u16.\n        fn (v: u16) =\u003e format!(\"u16: {}\", v),\n        // The expression for passing to above specializations.\n        expr,\n    )\n}\n\nfn main() {\n    assert_eq!(example(1.0), \"default value\");\n    assert_eq!(example(5u8), \"u8: 5\");\n    assert_eq!(example(10u16), \"u16: 10\");\n    println!(\"Done!\");\n}\n```\n\n`example` function roughly expands to below code. Note that exact expansion is\ninternal implementation detail. This example is provided to demonstrate how it\nworks under the hood.\n\n```rust,ignore\nfn example\u003cE\u003e(expr: E) -\u003e String {\n    trait SpecializedDispatchCall\u003cT\u003e {\n        fn dispatch(t: T) -\u003e String;\n    }\n\n    impl\u003cT\u003e SpecializedDispatchCall\u003cT\u003e for T {\n        default fn dispatch(_: T) -\u003e String {\n            format!(\"default value\")\n        }\n    }\n\n    impl SpecializedDispatchCall\u003cu8\u003e for u8 {\n        fn dispatch(v: u8) -\u003e String {\n            format!(\"u8: {}\", v)\n        }\n    }\n\n    impl SpecializedDispatchCall\u003cu8\u003e for u16 {\n        fn dispatch(v: u16) -\u003e String {\n            format!(\"u16: {}\", v)\n        }\n    }\n\n    \u003cE as SpecializedDispatchCall\u003cE\u003e\u003e::dispatch(expr)\n}\n```\n\nThe example above is [included][simple_example] in the repository.\n\nIt can be run with `cargo run --example simple_example`.\n\nExpanded code can be inspected using [`cargo-expand`]: `cargo expand --example\nsimple_example`.\n\n[simple_example]: examples/simple_example.rs\n[`cargo-expand`]: https://crates.io/crates/cargo-expand\n\n## Trait Bounds\n\nTrait bounds can be provided for the default case:\n\n```rust\n#![feature(min_specialization)]\n\nuse std::fmt::Display;\n\nuse specialized_dispatch::specialized_dispatch;\n\n// The expression type must also bind to the same trait.\nfn example\u003cE: Display\u003e(expr: E) -\u003e String {\n    specialized_dispatch!(\n        E -\u003e String,\n        // Notice the trait bound.\n        default fn \u003cT: Display\u003e(v: T) =\u003e {\n            format!(\"default value: {}\", v)\n        },\n        // Note that specializations also need to satisfy the same bound.\n        fn (v: u8) =\u003e format!(\"u8: {}\", v),\n        fn (v: u16) =\u003e format!(\"u16: {}\", v),\n        expr,\n    )\n}\n\nfn main() {\n    assert_eq!(example(1.5), \"default value: 1.5\");\n    assert_eq!(example(5u8), \"u8: 5\");\n    assert_eq!(example(10u16), \"u16: 10\");\n    println!(\"Done!\");\n}\n```\n\nLikewise, the example above is [included][trait_bound] in the repository.\n\nIt can be run with `cargo run --example trait_bound` or inspected with\n`cargo-expand`.\n\n[trait_bound]: examples/trait_bound.rs\n\n## Passing Extra Arguments\n\nExtra arguments can be passed to specializations. Argument types need to\ndeclared explicitly (i.e. they won't be captured automatically as it happens\nwith closures).\n\n```rust\n#![feature(min_specialization)]\n\nuse std::fmt::Display;\n\nuse specialized_dispatch::specialized_dispatch;\n\nfn example\u003cT: Display\u003e(expr: T, arg: \u0026str) -\u003e String {\n    specialized_dispatch!(\n        T -\u003e String,\n        default fn \u003cT: Display\u003e(v: T, arg: \u0026str) =\u003e {\n            format!(\"default value: {}, arg: {}\", v, arg)\n        },\n        fn (v: u8, arg: \u0026str) =\u003e format!(\"u8: {}, arg: {}\", v, arg),\n        fn (v: u16, arg: \u0026str) =\u003e format!(\"u16: {}, arg: {}\", v, arg),\n        expr, arg,\n    )\n}\n\nfn main() {\n    assert_eq!(example(1.5, \"I'm a\"), \"default value: 1.5, arg: I'm a\");\n    assert_eq!(example(5u8, \"walnut\"), \"u8: 5, arg: walnut\");\n    assert_eq!(example(10u16, \"tree\"), \"u16: 10, arg: tree\");\n    println!(\"Done!\");\n}\n```\n\nSpecialization still happens based on the first argument only.\n\nAs with previous examples, the example above is [included][pass_args] in the\nrepository as well. It can be run with `cargo run --example pass_args` or\ninspected with `cargo-expand`.\n\n[pass_args]: examples/pass_args.rs\n\n## Advanced Serdelike Example\n\nLet's say you are implementing a deserializer. There might be certain types\nthat work well with your own deserializer, while they have a default\nimplementation for generic deserializers (or even `unimplemented!` by default).\n\nTo simplify the example, we will create a watered-down version of relevant\n`serde` traits.\n\n```rust\n#![feature(min_specialization)]\n\nuse specialized_dispatch::specialized_dispatch;\n\n/// A simplified version of `serde::de::Deserializer`.\ntrait Deserializer\u003c'de\u003e {\n    type Error;\n\n    // Some generic deserializer functions...\n}\n\n/// A simplified version of `serde::de::Deserialize`.\ntrait Deserialize\u003c'de\u003e: Sized {\n    fn deserialize\u003cD\u003e(deserializer: D) -\u003e Result\u003cSelf, D::Error\u003e\n    where\n        D: Deserializer\u003c'de\u003e;\n}\n\n/// The node type we want to deserialize.\n#[derive(Debug)]\nstruct MyAwesomeNode;\n\n/// Our custom deserializer.\nstruct MyAwesomeDeserializer;\n\nimpl MyAwesomeDeserializer {\n    fn my_awesome_function(\u0026mut self) -\u003e MyAwesomeNode {\n        MyAwesomeNode\n    }\n}\n\nimpl Deserializer\u003c'_\u003e for MyAwesomeDeserializer {\n    type Error = ();\n    // Implement the generic deserializer functions...\n}\n\nimpl\u003c'de\u003e Deserialize\u003c'de\u003e for MyAwesomeNode {\n    fn deserialize\u003cD\u003e(deserializer: D) -\u003e Result\u003cSelf, D::Error\u003e\n    where\n        D: Deserializer\u003c'de\u003e,\n    {\n        Ok(specialized_dispatch! {\n            D -\u003e MyAwesomeNode,\n            // TODO(ozars): This causes rustc ICE.\n            // default fn \u003c'de, T: Deserializer\u003c'de\u003e\u003e(_deserializer: T) =\u003e {\n            default fn \u003cT\u003e(_deserializer: T) =\u003e {\n                unimplemented!()\n            },\n            fn (mut deserializer: MyAwesomeDeserializer) =\u003e {\n                // We can call a method from the concrete implementation here!\n                deserializer.my_awesome_function()\n            },\n            deserializer\n        })\n    }\n}\n\nfn main() {\n    println!(\"{:?}\", MyAwesomeNode::deserialize(MyAwesomeDeserializer));\n}\n```\n\nThe example above is [included][serdelike_example] in the repository. It can be\nrun with `cargo run --example serdelike_example` or inspected with\n`cargo-expand`.\n\n[serdelike_example]: examples/serdelike_example.rs\n\n## Limitations\n\n### Requires nightly\n\nThis is due to relying on `min_specialization` feature.\n\n### Only concrete types are supported for specialization\n\nSpecialization can be used only with concrete types (e.g. subtraits cannot be\nused for specialization). This is an existing limitation inherited from the\ncurrent implementation of `min_specialization` feature.\n\n### Variables aren't captured automatically\n\nThe macro expands its arms to some method implementations. As such, it cannot\nrefer to other variables in the scope where it's called from.\n\nHowever, extra arguments can be passed when they are explicitly declared in the\nmacro. Please refer to [Passing Extra Arguments](#passing-extra-arguments)\nsection.\n\n### Not working well with lifetimes\n\nI tried implementing lifetime support in various places, but I hit some\ncompiler errors and in some cases Internal Compiler Errors (ICE). See TODO in\n[Advanced Serdelike Example](#advanced-serdelike-example).\n\nThis is very likely due to underlying `min_specialization` implementation not\nbeing very mature yet, though it's quite possible I botched something somewhere\n(Please file an issue if you figure out which :P).\n\n## See also\n\n- [sagebind/castaway]: Safe, zero-cost downcasting for limited compile-time\n  specialization. This is an awesome library I've just stumbled upon, which\n  seems to be doing what is done here in a more robust and stable way.\n- [Autoref-based stable specialization]: A detailed case study for various methods\n  of specialization, introducing \"autoref-based specalization\".\n- [Generalized Autoref-Based Specialization]: Another method called\n  \"*autoderef*-based specialization\" which builds on top of the article above.\n\n[sagebind/castaway]: https://github.com/sagebind/castaway\n[Autoref-based stable specialization]: https://github.com/dtolnay/case-studies/tree/master/autoref-specialization\n[Generalized Autoref-Based Specialization]: https://lukaskalbertodt.github.io/2019/12/05/generalized-autoref-based-specialization.html \n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fozars%2Fspecialized-dispatch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fozars%2Fspecialized-dispatch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fozars%2Fspecialized-dispatch/lists"}