{"id":22286302,"url":"https://github.com/fadeevab/mediator-pattern-rust","last_synced_at":"2025-10-20T00:11:30.173Z","repository":{"id":52153503,"uuid":"520620751","full_name":"fadeevab/mediator-pattern-rust","owner":"fadeevab","description":"Mediator Pattern in Rust","archived":false,"fork":false,"pushed_at":"2024-07-16T18:47:37.000Z","size":213,"stargazers_count":16,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2024-07-16T22:56:03.579Z","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":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fadeevab.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":"2022-08-02T19:12:39.000Z","updated_at":"2024-07-16T18:47:40.000Z","dependencies_parsed_at":"2024-07-16T22:44:01.703Z","dependency_job_id":"7f302e16-c6f6-4d7c-a444-e5f4f9ce58fc","html_url":"https://github.com/fadeevab/mediator-pattern-rust","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fadeevab%2Fmediator-pattern-rust","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fadeevab%2Fmediator-pattern-rust/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fadeevab%2Fmediator-pattern-rust/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fadeevab%2Fmediator-pattern-rust/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fadeevab","download_url":"https://codeload.github.com/fadeevab/mediator-pattern-rust/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227961857,"owners_count":17847836,"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-12-03T16:55:27.593Z","updated_at":"2025-10-20T00:11:30.084Z","avatar_url":"https://github.com/fadeevab.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"_For other design patterns in Rust, see [https://github.com/fadeevab/design-patterns-rust](https://github.com/fadeevab/design-patterns-rust)._\n\n# Mediator Pattern in Rust\n\n_*Mediator* is a challenging pattern to be implemented in *Rust*._ Here is **why** and **what ways** are there to implement Mediator in Rust.\n\n## How to Run\n\n```bash\ncargo run --bin mediator-dynamic\ncargo run --bin mediator-static-recommended\n```\n\n## Execution Result\n\nOutput of the `mediator-static-recommended`.\n\n```\nPassenger train Train 1: Arrived\nFreight train Train 2: Arrival blocked, waiting\nPassenger train Train 1: Leaving\nFreight train Train 2: Arrived\nFreight train Train 2: Leaving\n'Train 3' is not on the station!\n```\n\n## Problem\n\nA typical Mediator implementation in other languages is a classic anti-pattern in Rust: many objects hold mutable cross-references on each other, trying to mutate each other, which is a deadly sin in Rust - the compiler won't pass your first naive implementation unless it's oversimplified.\n\nBy definition, [Mediator][1] restricts direct communications between the objects and forces them to collaborate only via a mediator object. It also stands for a Controller in the MVC pattern. Let's see the nice diagrams from https://refactoring.guru:\n\n| Problem                      | Solution                      |\n| ---------------------------- | ----------------------------- |\n| ![image](images/problem.png) | ![image](images/solution.png) |\n\nA common implementation in object-oriented languages looks like the following pseudo-code:\n\n```java\nController controller = new Controller();\n\n// Every component has a link to a mediator (controller).\ncomponent1.setController(controller);\ncomponent2.setController(controller);\ncomponent3.setController(controller);\n\n// A mediator has a link to every object.\ncontroller.add(component1);\ncontroller.add(component2);\ncontroller.add(component2);\n```\n\nNow, let's read this in **Rust** terms: _\"**mutable** structures have **mutable** references to a **shared mutable** object (mediator) which in turn has mutable references back to those mutable structures\"_.\n\nBasically, you can start to imagine the unfair battle against the Rust compiler and its borrow checker. It seems like a solution introduces more problems:\n\n![image](images/mediator-mut-problem.png)\n\n1. Imagine that the control flow starts at point 1 (Checkbox) where the 1st **mutable** borrow happens.\n2. The mediator (Dialog) interacts with another object at point 2 (TextField).\n3. The TextField notifies the Dialog back about finishing a job and that leads to a **mutable** action at point 3... Bang!\n\nThe second mutable borrow breaks the compilation with an error (the first borrow was on the point 1).\n\n**In Rust, a widespread Mediator implementation is mostly an anti-pattern.**\n\n## Existing Primers\n\nYou might see a reference Mediator examples in Rust like [this][5]: the example is too much synthetic - there are no mutable operations, at least at the level of trait methods.\n\nThe [rust-unofficial/patterns](https://github.com/rust-unofficial/patterns) repository doesn't include a referenced Mediator pattern implementation as of now, see the [Issue #233][2].\n\n**Nevertheless, we don't surrender.**\n\n## Cross-Referencing with `Rc\u003cRefCell\u003c..\u003e\u003e`\n\nThere is an example of a [Station Manager example in Go][4]. Trying to make it with Rust leads to mimicking a typical OOP through reference counting and borrow checking with mutability in runtime (which has quite unpredictable behavior in runtime with panics here and there).\n\n👉 Here is a Rust implementation: [mediator-dynamic](https://github.com/fadeevab/mediator-pattern-rust/mediator-dynamic)\n\n🏁 I would recommend this approach for applications that need **multi-threaded support**: particular components after being added to the mediator can be sent to different threads and modified from there.\n\n📄 Real-world example: [`indicatif::MultiProgress`](https://docs.rs/indicatif/latest/indicatif/struct.MultiProgress.html) (mediates progress bars with support of being used in multiple threads).\n\nKey points:\n\n1. All trait methods look like read-only (`\u0026self`): immutable `self` and parameters.\n2. `Rc`, `RefCell` are extensively used under the hood to take responsibility for the mutable borrowing from compilation time to runtime. Invalid implementation will lead to panic in runtime.\n\n\n## Top-Down Ownership\n\n☝ The key point is thinking in terms of OWNERSHIP.\n\n![Ownership](images/mediator-rust-approach.jpg)\n\n1. A mediator takes ownership of all components.\n2. A component doesn't preserve a reference to a mediator. Instead, it gets the reference via a method call.\n    ```rust\n    // A train gets a mediator object by reference.\n    pub trait Train {\n        fn name(\u0026self) -\u003e \u0026String;\n        fn arrive(\u0026mut self, mediator: \u0026mut dyn Mediator);\n        fn depart(\u0026mut self, mediator: \u0026mut dyn Mediator);\n    }\n\n    // Mediator has notification methods.\n    pub trait Mediator {\n        fn notify_about_arrival(\u0026mut self, train_name: \u0026str) -\u003e bool;\n        fn notify_about_departure(\u0026mut self, train_name: \u0026str);\n    }\n    ```\n3. Control flow starts from `fn main()` where the mediator receives external events/commands.\n4. `Mediator` trait for the interaction between components (`notify_about_arrival`, `notify_about_departure`) is not the same as its external API for receiving external events (`accept`, `depart` commands from the main loop).\n    ```rust\n    let train1 = PassengerTrain::new(\"Train 1\");\n    let train2 = FreightTrain::new(\"Train 2\");\n\n    // Station has `accept` and `depart` methods,\n    // but it also implements `Mediator`.\n    let mut station = TrainStation::default();\n\n    // Station is taking ownership of the trains.\n    station.accept(train1);\n    station.accept(train2);\n\n    // `train1` and `train2` have been moved inside,\n    // but we can use train names to depart them.\n    station.depart(\"Train 1\");\n    station.depart(\"Train 2\");\n    station.depart(\"Train 3\");\n    ```\n\nA few changes to the direct approach leads to a safe mutability being checked at compilation time.\n\n👉 A Train Station primer **without** `Rc`, `RefCell` tricks, but **with** `\u0026mut self` and compiler-time borrow checking: https://github.com/fadeevab/mediator-pattern-rust/mediator-static-recommended.\n\n👉 A real-world example of such approach: [Cursive (TUI)][5].\n\n[1]: https://refactoring.guru/design-patterns/mediator\n[2]: https://github.com/rust-unofficial/patterns/issues/233\n[3]: https://chercher.tech/rust/mediator-design-pattern-rust\n[4]: https://refactoring.guru/design-patterns/mediator/go/example\n[5]: https://crates.io/crates/cursive\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffadeevab%2Fmediator-pattern-rust","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffadeevab%2Fmediator-pattern-rust","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffadeevab%2Fmediator-pattern-rust/lists"}