{"id":34565718,"url":"https://github.com/seuros/matryoshka","last_synced_at":"2026-01-23T14:02:59.928Z","repository":{"id":319713050,"uuid":"1079275448","full_name":"seuros/matryoshka","owner":"seuros","description":"Ruby ↔ Rust Design Patterns: FFI Hybrid and Mirror API patterns for building polyglot gems","archived":false,"fork":false,"pushed_at":"2025-11-17T14:55:55.000Z","size":58,"stargazers_count":45,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-01-18T00:22:20.167Z","etag":null,"topics":["design-patterns","embedded-systems","esp32","ffi","gem-development","learning","magnus","no-std","performance","polyglot","ruby","rust"],"latest_commit_sha":null,"homepage":null,"language":null,"has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/seuros.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-19T13:36:42.000Z","updated_at":"2026-01-05T23:37:11.000Z","dependencies_parsed_at":null,"dependency_job_id":"f8907479-65a4-474d-9253-209263ca8236","html_url":"https://github.com/seuros/matryoshka","commit_stats":null,"previous_names":["seuros/matryoshka"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/seuros/matryoshka","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seuros%2Fmatryoshka","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seuros%2Fmatryoshka/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seuros%2Fmatryoshka/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seuros%2Fmatryoshka/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/seuros","download_url":"https://codeload.github.com/seuros/matryoshka/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/seuros%2Fmatryoshka/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28693458,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-23T11:01:27.039Z","status":"ssl_error","status_checked_at":"2026-01-23T11:00:26.909Z","response_time":59,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.6:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["design-patterns","embedded-systems","esp32","ffi","gem-development","learning","magnus","no-std","performance","polyglot","ruby","rust"],"created_at":"2025-12-24T09:04:04.273Z","updated_at":"2026-01-23T14:02:59.899Z","avatar_url":"https://github.com/seuros.png","language":null,"funding_links":[],"categories":["Others"],"sub_categories":[],"readme":"# Matryoshka: Ruby ↔ Rust Design Patterns\n\n**Build Ruby gems that learn Rust for free.**\n\nTwo design patterns for Ruby/Rust collaboration:\n1. **FFI Hybrid** - Ruby wraps Rust for optional 10-100x speedup\n2. **Mirror API** - Parallel Ruby/Rust implementations with conceptual parity\n\n---\n\n## Why \"Matryoshka\"?\n\n**Matryoshka dolls** (Russian nesting dolls) share the same design across different scales. This repo's patterns embody that concept in two ways:\n\n### Pattern 1: Nested Dolls (FFI Hybrid)\n\n```mermaid\ngraph TB\n    subgraph Ruby[\"Ruby Gem (Outer doll)\"]\n        API[Public API]\n        subgraph FFI[\"FFI Layer (Middle doll)\"]\n            Magnus[Magnus Bridge]\n            subgraph Core[\"Rust Core (Inner doll)\"]\n                Logic[no_std Logic]\n            end\n        end\n    end\n    API --\u003e Magnus --\u003e Logic\n\n    style Ruby fill:#e74c3c,stroke:#c0392b,color:#fff\n    style FFI fill:#e67e22,stroke:#d35400,color:#fff\n    style Core fill:#f39c12,stroke:#e67e22,color:#fff\n```\n\n**Same logic, nested layers.** The Ruby API wraps FFI which wraps Rust. Each layer looks the same to the user.\n\n### Pattern 2: Twin Dolls (Mirror API)\n\n```mermaid\ngraph LR\n    subgraph Ruby[\"Ruby Gem\"]\n        RubyAPI[Same API]\n        RubyImpl[Mutation-based\u003cbr/\u003eRuntime-checked\u003cbr/\u003eFramework-aware]\n    end\n\n    subgraph Rust[\"Rust Crate\"]\n        RustAPI[Same API]\n        RustImpl[Typestate-based\u003cbr/\u003eCompile-checked\u003cbr/\u003eno_std ready]\n    end\n\n    Ruby -.-\u003e|Same Concept| Rust\n\n    style Ruby fill:#c0392b,stroke:#a93226,color:#fff\n    style Rust fill:#d35400,stroke:#ba4a00,color:#fff\n```\n\n**Same design, parallel implementations.** Two independent projects that share the same conceptual API, each optimized for its language.\n\n**Both are matryoshka** because they maintain **conceptual identity** across different forms—whether nested (FFI) or parallel (Mirror).\n\n---\n\n## Quick Start\n\n### FFI Hybrid Pattern (ChronoMachines)\n\n**What it does:** Ruby gem with optional Rust acceleration. Falls back to pure Ruby gracefully.\n\n**Ruby usage:**\n```ruby\n# Works everywhere (no Cargo, compilation fails gracefully)\nChronoMachines.retry(max_attempts: 5, base_delay: 0.1) do\n  risky_operation\nend\n```\n\n**Under the hood:**\n- Pure Ruby implementation (fallback)\n- Rust FFI speedup (65x faster when available)\n- `no_std` core (compiles to ESP32)\n\n**When to use:** Compute-heavy code (parsing, crypto, math) where the algorithm is identical in both languages.\n\n👉 **[Read FFI Hybrid Guide](FFI_HYBRID.md)**\n\n---\n\n### Mirror API Pattern (state_machines)\n\n**What it does:** Two independent implementations (Ruby + Rust) with 90%+ feature parity, NO FFI.\n\n**Ruby version:**\n```ruby\nstate_machine :state, initial: :parked do\n  event :ignite { transition parked: :idling }\nend\n\nvehicle.ignite      # Mutates in place\nvehicle.state       # =\u003e \"idling\"\n```\n\n**Rust version:**\n```rust\nstate_machine! {\n    initial: Parked,\n    events {\n        ignite { transition: { from: Parked, to: Idling } }\n    }\n}\n\nlet v1 = Vehicle::new(());            // Type: Vehicle\u003cParked\u003e\nlet v2 = v1.ignite().unwrap();        // Type: Vehicle\u003cIdling\u003e\n// v1 consumed, v2 has different type\n```\n\n**When to use:** When ownership semantics differ (mutation vs. consumption), or when FFI would destroy type safety guarantees.\n\n👉 **[Read Mirror API Guide](MIRROR_API.md)**\n\n---\n\n## Philosophy\n\n### 1. Performance is Optional\nIf your Ruby code was \"good enough\" at 1x speed, it's **amazing** at 65x.\n\n### 2. Embedded is Free\nWrite logic in Ruby → Compile Rust core to ESP32. No C required.\n\n### 3. Learning is Implicit\nRuby devs read Rust ports → Understand 40% of Rust syntax by osmosis.\n\n### 4. Readability Over Cleverness\n- ✅ Explicit variable bindings\n- ✅ Ruby-like method names\n- ❌ No `.fetch().then().map()` chains\n- ❌ No \"clever\" APIs that sacrifice clarity\n\n👉 **[Read Philosophy](PHILOSOPHY.md)**\n\n---\n\n## Safety: Can It Segfault CRuby?\n\n**Short answer:** The architecture makes segfaults extremely difficult.\n\n**Why this is safer than C extensions:**\n\nTraditional C extensions directly manipulate Ruby VM internals:\n```c\n// ❌ \"Sus\" C extension pattern\nVALUE some_method(VALUE self) {\n    rb_funcall(obj, rb_intern(\"method\"), 0);  // Calling Ruby VM\n    VALUE result = rb_str_new(...);            // Manual allocation\n    RARRAY_PTR(ary)[i] = ...;                  // Direct pointer access\n}\n```\n\n**Matryoshka FFI Hybrid pattern:**\n```rust\n// ✅ Rust core (no Ruby knowledge)\npub fn calculate_delay(\u0026self, attempt: u8) -\u003e u64 {\n    // Pure math, no allocations, no Ruby types\n}\n\n// ✅ FFI layer (Magnus handles safety)\nfn calculate_delay_native(attempt: i64) -\u003e f64 {\n    let result = core::calculate_delay(attempt as u8);\n    result as f64  // Magnus converts safely\n}\n```\n\n**Architecture guarantees:**\n\n1. **No direct Ruby VM calls** - Magnus abstracts all `rb_*` functions\n2. **No manual GC interaction** - Rust never touches Ruby's garbage collector\n3. **Only primitives cross FFI boundary** - `i64`, `f64`, `String` (copied, not borrowed)\n4. **Rust core is isolated** - `no_std` crate with no Ruby types\n5. **Type conversions are explicit** - Magnus enforces compile-time safety\n\n**Failure modes (and mitigations):**\n\n| Scenario | C Extension | Matryoshka |\n|----------|-------------|------------|\n| Panic/crash | Segfault | Magnus catches, converts to Ruby exception |\n| Bad type | Runtime crash | Compile-time error (Magnus type checking) |\n| Memory leak | Easy (forget to free) | Impossible (Rust ownership) |\n| GC bug | Holding pointers across GC | No Ruby heap access |\n| Race condition | Undefined behavior | Document thread-safety (same as Ruby) |\n\n**Compare to pg/mysql2/trilogy:**\nThose gems call C libraries (`libpq`, `libmysqlclient`) and carefully manage Ruby GC, exceptions, and memory. Much larger \"sus\" surface area.\n\n**Matryoshka's promise:** If the Rust core is `no_std` and only passes primitives across FFI, segfaults are **architecturally prevented**, not just \"avoided by good coding.\"\n\n**Real example (ChronoMachines):**\n- Input: `i64`, `f64` (primitives from Ruby)\n- Computation: Pure Rust math (no allocations, no Ruby VM)\n- Output: `f64` (primitive to Ruby)\n- **No way to segfault** - no pointers, no Ruby VM access, no GC interaction\n\n---\n\n## Pattern Decision Tree\n\n```mermaid\nflowchart TD\n    Start[Need Ruby + Rust\u003cbr/\u003ecollaboration?]\n    Q1{Same algorithm,\u003cbr/\u003ejust need speed?}\n    Q2{Different semantics/\u003cbr/\u003eownership models?}\n\n    FFI[Use FFI Hybrid]\n    FFIBenefits[✅ Optional performance boost\u003cbr/\u003e✅ Graceful fallback to pure Ruby\u003cbr/\u003e✅ Share Rust core with embedded]\n\n    Mirror[Use Mirror API]\n    MirrorBenefits[✅ Language-appropriate idioms\u003cbr/\u003e✅ Preserve type safety\u003cbr/\u003e✅ Independent evolution]\n\n    Start --\u003e Q1\n    Q1 --\u003e|Yes| FFI\n    FFI --\u003e FFIBenefits\n    Q1 --\u003e|No| Q2\n    Q2 --\u003e|Yes| Mirror\n    Mirror --\u003e MirrorBenefits\n\n    style FFI fill:#f39c12,stroke:#e67e22,color:#fff\n    style Mirror fill:#d35400,stroke:#ba4a00,color:#fff\n    style FFIBenefits fill:#fff3cd,stroke:#856404\n    style MirrorBenefits fill:#f8d7da,stroke:#721c24\n```\n\n---\n\n## Examples\n\n### FFI Hybrid\n- **[chrono_machines](https://github.com/seuros/chrono_machines)** - Retry logic with exponential backoff (reference implementation)\n- `examples/simple_parser/` - Minimal string parsing example\n- `examples/embedded_blinker/` - ESP32 using shared Rust core\n\n### Mirror API\n- **[state_machines](https://github.com/state-machines/state_machines)** (Ruby) + **[state-machines-rs](https://github.com/state-machines/state-machines-rs)** (Rust)\n- Identical state machine DSL\n- Ruby: ActiveRecord integration, runtime flexibility\n- Rust: Compile-time type safety, embedded targets\n\n---\n\n## Learn Rust by Accident\n\n**Ruby:**\n```ruby\ndef calculate_delay(attempts)\n  base = @base_delay * (@multiplier ** (attempts - 1))\n  [base, @max_delay].min * (1 + rand)\nend\n```\n\n**Rust (intentionally similar):**\n```rust\nfn calculate_delay(\u0026self, attempts: u8) -\u003e f64 {\n    let base = self.base_delay * self.multiplier.powi((attempts - 1) as i32);\n    base.min(self.max_delay) * (1.0 + rng.gen())\n}\n```\n\n**What you learn:**\n- `let` = variable binding\n- `fn name(\u0026self, param: Type) -\u003e ReturnType` = method signature\n- `.powi()` = integer exponent (like `**`)\n- `.min()` = same as Ruby\n- Last expression returns (no `return` needed)\n\nAfter reading 3-4 ported methods, you understand **40% of Rust syntax**.\n\n👉 **[Ruby→Rust Translation Guide](SYNTAX.md)**\n\n---\n\n## When to Use These Patterns\n\n### ✅ Perfect For\n\n**FFI Hybrid:**\n- Parsers (JSON, XML, CSV, Markdown)\n- String manipulation (regex, sanitization)\n- Cryptography (hashing, encoding, JWT)\n- Math-heavy algorithms (statistics, simulations)\n- Date/time conversions\n\n**Mirror API:**\n- State machines (when type safety matters)\n- Protocol implementations (different ownership models)\n- Educational projects (teaching Rust via Ruby)\n- Embedded + server dual-target libraries\n\n### ❌ Don't Use For\n\n- Pure metaprogramming gems (ActiveSupport)\n- Network clients (latency dominates, not CPU)\n- Simple wrappers around system commands\n- Gems with \u003c 3 compute-intensive methods\n\n---\n\n## Getting Started\n\n1. **Understand the philosophy** → [PHILOSOPHY.md](PHILOSOPHY.md)\n2. **Choose your pattern:**\n   - FFI Hybrid → [FFI_HYBRID.md](FFI_HYBRID.md)\n   - Mirror API → [MIRROR_API.md](MIRROR_API.md)\n3. **Learn Rust syntax** → [SYNTAX.md](SYNTAX.md)\n4. **Study examples** → `examples/` directory\n5. **Use templates** → `templates/` directory\n\n---\n\n## Project Structure\n\n```\nmatryoshka/\n├── README.md                    # You are here\n├── PHILOSOPHY.md                # Why this exists\n├── FFI_HYBRID.md                # ChronoMachines pattern\n├── MIRROR_API.md                # state_machines pattern\n├── SYNTAX.md                    # Ruby→Rust cheatsheet\n├── examples/\n│   ├── chrono_machines/         # FFI Hybrid reference\n│   ├── state_machines/          # Mirror API reference\n│   ├── simple_parser/           # Minimal FFI example\n│   └── embedded_blinker/        # ESP32 demo\n└── templates/\n    ├── ffi_hybrid/              # FFI pattern scaffolding\n    └── mirror_api/              # Mirror pattern scaffolding\n```\n\n---\n\n## Real-World Benefits\n\n### ChronoMachines (FFI Hybrid)\n- **65x faster** retry delay calculations\n- **Graceful fallback** when native extension unavailable (Magnus uses C extensions, not supported by JRuby)\n- Rust core compiles to **ESP32** (same retry logic in firmware)\n- Zero changes to public API\n\n### state_machines (Mirror API)\n- **Ruby version:** 7,050 LOC, Rails integration, runtime flexibility\n- **Rust version:** 39,711 LOC, compile-time type safety, `no_std` support\n- **90%+ feature parity** despite different ownership models\n- Ruby devs learning Rust via familiar patterns\n\n---\n\n## Contributing\n\nWe welcome:\n- ✅ Example gems using these patterns\n- ✅ Documentation improvements\n- ✅ Embedded platform guides (ESP32, STM32, RP2040)\n- ✅ Translation guides for other languages\n\nWe reject:\n- ❌ \"Clever\" Rust code that's hard to read\n- ❌ Breaking `no_std` compatibility in FFI Hybrid cores\n- ❌ Performance-only optimizations that sacrifice clarity\n\n---\n\n## License\n\nMIT (copy freely, attribution appreciated)\n\n---\n\n## Questions?\n\n- **\"Why not just use C extensions?\"** → **C doesn't have the crate ecosystem.** In traditional C extensions (pg, trilogy, mysql2), the C code is tightly coupled to Ruby—it's just extension code, not a reusable library. With Matryoshka, the **Rust crate is a fully functional, standalone library** that can be published to [crates.io](https://crates.io) and used in pure Rust projects (embedded, CLI tools, other libraries). The Ruby gem is just ONE consumer of it. The crate has independent value beyond Ruby.\n\n- **\"Why not FFI for everything?\"** → Some patterns (like typestate) lose their value across FFI. When ownership semantics differ fundamentally (Ruby's mutation vs Rust's consumption), parallel implementations (Mirror API) preserve language-specific guarantees that FFI would destroy.\n\n- **\"Why the Russian doll metaphor?\"** → See [top of this README](#why-matryoshka). **TL;DR:** Matryoshka dolls share the same design across different scales—same concept for nested layers (FFI Hybrid) and parallel implementations (Mirror API).\n\n- **\"Can I use this in production?\"** → Yes. ChronoMachines is production-tested with graceful fallbacks.\n\n- **\"Do I need to know Rust first?\"** → No. Read Ruby code, then read Rust port side-by-side. Learn by comparison (~40% syntax coverage after 3-4 methods).\n\n- **\"What about Zig?\"** → Zig can provide speedup, but lacks the packaging story. Matryoshka lets gems consume published crates from [crates.io](https://crates.io). Zig is repo-dependent (no central registry). For Zig FFI anyway: [zig.rb](https://github.com/furunkel/zig.rb).\n\n---\n\n**Start here:** [PHILOSOPHY.md](PHILOSOPHY.md) → Understand the \"why\" before the \"how\"\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseuros%2Fmatryoshka","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fseuros%2Fmatryoshka","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fseuros%2Fmatryoshka/lists"}