{"id":17336575,"url":"https://github.com/restioson/spaad","last_synced_at":"2026-03-13T18:36:13.939Z","repository":{"id":55141522,"uuid":"270040130","full_name":"Restioson/spaad","owner":"Restioson","description":" ⚛️ Zero-boilerplate actor systems with xtra","archived":false,"fork":false,"pushed_at":"2023-02-15T11:22:07.000Z","size":83,"stargazers_count":37,"open_issues_count":5,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-04-29T20:06:57.168Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Restioson.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":"2020-06-06T16:37:09.000Z","updated_at":"2024-02-14T16:27:36.000Z","dependencies_parsed_at":"2024-10-15T15:41:44.072Z","dependency_job_id":null,"html_url":"https://github.com/Restioson/spaad","commit_stats":{"total_commits":41,"total_committers":5,"mean_commits":8.2,"dds":0.1707317073170732,"last_synced_commit":"81888f86442704b82d81c52a2215d0814ac54a6b"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Restioson%2Fspaad","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Restioson%2Fspaad/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Restioson%2Fspaad/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Restioson%2Fspaad/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Restioson","download_url":"https://codeload.github.com/Restioson/spaad/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248886324,"owners_count":21177643,"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":[],"created_at":"2024-10-15T15:31:29.625Z","updated_at":"2026-03-13T18:36:13.871Z","avatar_url":"https://github.com/Restioson.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# spaad\n_**Sp**ooky **a**ction **a**t a **d**istance_\n\nspaad is a crate which removes the vast majority of boilerplate in [xtra](https://github.com/Restioson/xtra), a tiny\nactor framework. It does this through a proc macro: `spaad::entangled`. The effect is that both writing a message handler\nand calling it looks nigh-identical to a traditional method call.\n\nFor instance, a handler looks like this:\n```rust\n#[spaad::entangled]\nimpl MyActor {\n    async fn print(\u0026mut self) { /* ... */ }\n}\n```\n\nand is called like this:\n\n```rust\nmy_actor.print().await;\n```\n\n## Usage\n\nThe proc macro `spaad::entangled` is the core item of spaad. It creates the messages and `Handler` implementations for\neach handler from its signature, as well as a struct wrapping the address, which has ergonomic function names for sending messages.\nThe end result is that it looks as though no actors are involved to both the caller and callee. The name of the crate is\na cheeky reference to what Einstein called quantum entanglement - \"_**sp**ooky **a**ction **a**t a **d**istance_\" - since\nin quantum entanglement it also appears that one particle's state is \"magically\" changed.\n\nThis macro is used as an attribute on the actor struct definition and its impl blocks.\n\n## Example \n```rust\nuse xtra::prelude::*;\n\n#[spaad::entangled]\npub struct Printer {\n    times: usize,\n}\n\n#[spaad::entangled]\nimpl Actor for Printer {}\n\n#[spaad::entangled]\nimpl Printer {\n    #[spaad::spawn]\n    pub fn new() -\u003e Self {\n        Printer { times: 0 }\n    }\n\n    #[spaad::handler]\n    pub fn print(\u0026mut self, to_print: String) {\n        self.times += 1;\n        println!(\n            \"Printing {}. Printed {} times so far.\",\n            to_print, self.times\n        );\n    }\n}\n\n#[tokio::main]\nasync fn main() {\n    // a Spawner must be provided as the last argument here\n    let printer = Printer::new(\u0026mut xtra::spawner::Tokio::Global);\n\n    loop {\n        printer.print(\"hello\".to_string()).await;\n    }\n}\n```\n\nThe generated `Printer` type does not, in fact, contain all members of the strucy, but rather its address.\nThe actual structure is strictly internal and cannot be interacted with except by sending messages or from inside its\nimpl blocks. When referred to inside of impl blocks as a type, `Self` must be used, as it will be renamed.\n\nIt is important to note that the `new` function is a special case. If it is present, the proc macro will also emit a\n`create` method for the actor wrapper, corresponding to `Actor::create`. It can take arguments. If the `with-tokio-0_2`\nor `with-async_std-1_0` features are enabled, then it will also emit a `new` method, which corresponds to `Actor::spawn`.\n\nIf you do not want to `await` for the message to complete processing, you can do the following:\n```rust\nlet _ = my_actor.print(); // Binding to avoid #[must_use] warning on Future\n```\n\nFor a more complex example, such as handling the actor's disconnection and taking `Context` in a handler, see the\ndocumentation or `complex.rs` in the examples directory. To see the generated code, run `cargo +nightly doc` in the \n`example_generated` folder.\n\n## Advantages\n\n- More ergonomic and concise.\n- IDE support. It is possible in some IDEs (only tested on IntelliJ IDEA with the Rust plugin) to jump to definition.\n  This is only partial, as in some cases the IDE does not understand the change in the mutability of the `self` parameter\n  (only `\u0026self` is required when sending a message, but it looks as though it is declared `\u0026mut self`).\n- Can use nightly API with less chance of UB (by removing an opportunity for UB in GATs), and in a completely transparent\n  manner. \n\n## Disadvantages\n\n- Similar caveat to xtra itself: immaturity.\n- IDE support is not full. In some cases, there can be issues. This could be resolved by proc macro expansion, but that\n  appears to be a way off. It appears that Rust Analyzer handles this slightly better than IntelliJ Rust, though this\n  may change.\n\n## Nightly API\n\nIn order to enable the xtra nightly API, disable the default `stable` feature in your `Cargo.toml`.\n\n## Version compatibility\n\nSpaad 0.4.0 is compatible with xtra 0.5.0.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frestioson%2Fspaad","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frestioson%2Fspaad","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frestioson%2Fspaad/lists"}