[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-dependency-injection-rust-service-locator-arc-trait-object":3},{"article":4,"author":52},{"id":5,"category_id":6,"title":7,"slug":8,"excerpt":9,"content_md":10,"content_html":11,"locale":12,"author_id":13,"published":14,"published_at":15,"meta_title":7,"meta_description":16,"focus_keyword":17,"og_image":18,"canonical_url":18,"robots_meta":19,"created_at":15,"updated_at":15,"tags":20,"category_name":30,"related_articles":31},"d2000000-0000-0000-0000-000000000122","a0000000-0000-0000-0000-000000000026","Dependency Injection di Rust — ServiceLocator, Arc, dan Trait Object","dependency-injection-rust-service-locator-arc-trait-object","Pola dependency injection di Rust: trait sebagai interface, Arc untuk shared ownership, impl Trait vs dyn Trait, dan bagaimana menstruktur service layer untuk testability.","## Dependency Injection di Rust\n\nRust tidak memiliki framework DI seperti Spring (Java) atau .NET. Tetapi sistem trait Rust menyediakan mekanisme DI yang kuat dan type-safe. Kuncinya: definisikan behavior sebagai trait, terima trait sebagai parameter, dan injeksi implementasi konkret saat runtime.\n\n## Trait sebagai Interface\n\n```rust\n#[async_trait]\npub trait UserRepository: Send + Sync {\n    async fn find_by_id(&self, id: Uuid) -> Result\u003COption\u003CUser>, DbError>;\n    async fn create(&self, user: CreateUser) -> Result\u003CUser, DbError>;\n    async fn update(&self, id: Uuid, data: UpdateUser) -> Result\u003CUser, DbError>;\n    async fn delete(&self, id: Uuid) -> Result\u003C(), DbError>;\n}\n\n#[async_trait]\npub trait EmailService: Send + Sync {\n    async fn send(&self, to: &str, subject: &str, body: &str) -> Result\u003C(), EmailError>;\n}\n```\n\n## Implementasi Konkret\n\n```rust\npub struct PgUserRepository {\n    pool: PgPool,\n}\n\n#[async_trait]\nimpl UserRepository for PgUserRepository {\n    async fn find_by_id(&self, id: Uuid) -> Result\u003COption\u003CUser>, DbError> {\n        sqlx::query_as::\u003C_, User>(\"SELECT * FROM users WHERE id = $1\")\n            .bind(id)\n            .fetch_optional(&self.pool)\n            .await\n            .map_err(DbError::from)\n    }\n    \u002F\u002F ... implementasi lainnya\n}\n\npub struct SmtpEmailService {\n    host: String,\n    port: u16,\n}\n\n#[async_trait]\nimpl EmailService for SmtpEmailService {\n    async fn send(&self, to: &str, subject: &str, body: &str) -> Result\u003C(), EmailError> {\n        \u002F\u002F Kirim email via SMTP\n        Ok(())\n    }\n}\n```\n\n## Service Layer dengan DI\n\n```rust\npub struct UserService\u003CR: UserRepository, E: EmailService> {\n    repo: R,\n    email: E,\n}\n\nimpl\u003CR: UserRepository, E: EmailService> UserService\u003CR, E> {\n    pub fn new(repo: R, email: E) -> Self {\n        Self { repo, email }\n    }\n    \n    pub async fn register(&self, data: CreateUser) -> Result\u003CUser, AppError> {\n        let user = self.repo.create(data).await?;\n        self.email.send(\n            &user.email,\n            \"Selamat datang!\",\n            \"Terima kasih telah mendaftar.\"\n        ).await?;\n        Ok(user)\n    }\n}\n```\n\n## Arc untuk Shared Ownership\n\nDalam aplikasi web, service sering di-share antar request handler. `Arc` memungkinkan shared ownership yang thread-safe:\n\n```rust\ntype DynUserRepo = Arc\u003Cdyn UserRepository>;\ntype DynEmailService = Arc\u003Cdyn EmailService>;\n\n#[derive(Clone)]\npub struct AppState {\n    pub user_repo: DynUserRepo,\n    pub email_svc: DynEmailService,\n}\n\nimpl AppState {\n    pub fn new(pool: PgPool, smtp_config: SmtpConfig) -> Self {\n        Self {\n            user_repo: Arc::new(PgUserRepository { pool: pool.clone() }),\n            email_svc: Arc::new(SmtpEmailService {\n                host: smtp_config.host,\n                port: smtp_config.port,\n            }),\n        }\n    }\n}\n```\n\n## impl Trait vs dyn Trait\n\n**impl Trait (static dispatch):**\n```rust\nfn process(repo: &impl UserRepository) { ... }\n\u002F\u002F Dikompilasi menjadi fungsi terpisah per tipe konkret\n\u002F\u002F Zero-cost abstraction, tapi tidak bisa menyimpan tipe berbeda\n```\n\n**dyn Trait (dynamic dispatch):**\n```rust\nfn process(repo: &dyn UserRepository) { ... }\n\u002F\u002F Menggunakan vtable untuk dispatch saat runtime\n\u002F\u002F Fleksibel, overhead minimal (~2ns per panggilan)\n```\n\nGunakan `dyn Trait` ketika:\n- Menyimpan di struct sebagai field\n- Menyimpan di koleksi heterogen\n- Memerlukan dependency injection saat runtime\n\nGunakan `impl Trait` ketika:\n- Parameter fungsi dengan tipe yang diketahui saat kompilasi\n- Return type fungsi\n- Performa kritis (menghindari vtable lookup)\n\n## Testing dengan Mock\n\n```rust\n#[cfg(test)]\nmod tests {\n    use super::*;\n    \n    struct MockUserRepo {\n        users: Mutex\u003CVec\u003CUser>>,\n    }\n    \n    #[async_trait]\n    impl UserRepository for MockUserRepo {\n        async fn find_by_id(&self, id: Uuid) -> Result\u003COption\u003CUser>, DbError> {\n            let users = self.users.lock().unwrap();\n            Ok(users.iter().find(|u| u.id == id).cloned())\n        }\n        async fn create(&self, data: CreateUser) -> Result\u003CUser, DbError> {\n            let user = User {\n                id: Uuid::new_v4(),\n                email: data.email,\n                name: data.name,\n            };\n            self.users.lock().unwrap().push(user.clone());\n            Ok(user)\n        }\n        \u002F\u002F ...\n    }\n    \n    struct MockEmailService {\n        sent: Mutex\u003CVec\u003C(String, String)>>,\n    }\n    \n    #[async_trait]\n    impl EmailService for MockEmailService {\n        async fn send(&self, to: &str, subject: &str, _body: &str) -> Result\u003C(), EmailError> {\n            self.sent.lock().unwrap().push((to.to_string(), subject.to_string()));\n            Ok(())\n        }\n    }\n    \n    #[tokio::test]\n    async fn test_register_sends_email() {\n        let repo = MockUserRepo { users: Mutex::new(vec![]) };\n        let email = MockEmailService { sent: Mutex::new(vec![]) };\n        let service = UserService::new(repo, email);\n        \n        let user = service.register(CreateUser {\n            email: \"test@example.com\".into(),\n            name: \"Test User\".into(),\n        }).await.unwrap();\n        \n        assert_eq!(user.email, \"test@example.com\");\n        \u002F\u002F Verifikasi email dikirim\n        let sent = service.email.sent.lock().unwrap();\n        assert_eq!(sent.len(), 1);\n        assert_eq!(sent[0].0, \"test@example.com\");\n    }\n}\n```\n\n## Kesimpulan\n\nDependency injection di Rust menggunakan trait sebagai interface, Arc untuk shared ownership, dan dyn Trait untuk dispatch runtime. Pola ini memberikan testability tinggi tanpa framework DI eksternal — memanfaatkan kekuatan sistem tipe Rust untuk keamanan kompilasi dan zero-cost abstraction.","\u003Ch2 id=\"dependency-injection-di-rust\">Dependency Injection di Rust\u003C\u002Fh2>\n\u003Cp>Rust tidak memiliki framework DI seperti Spring (Java) atau .NET. Tetapi sistem trait Rust menyediakan mekanisme DI yang kuat dan type-safe. Kuncinya: definisikan behavior sebagai trait, terima trait sebagai parameter, dan injeksi implementasi konkret saat runtime.\u003C\u002Fp>\n\u003Ch2 id=\"trait-sebagai-interface\">Trait sebagai Interface\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-rust\">#[async_trait]\npub trait UserRepository: Send + Sync {\n    async fn find_by_id(&amp;self, id: Uuid) -&gt; Result&lt;Option&lt;User&gt;, DbError&gt;;\n    async fn create(&amp;self, user: CreateUser) -&gt; Result&lt;User, DbError&gt;;\n    async fn update(&amp;self, id: Uuid, data: UpdateUser) -&gt; Result&lt;User, DbError&gt;;\n    async fn delete(&amp;self, id: Uuid) -&gt; Result&lt;(), DbError&gt;;\n}\n\n#[async_trait]\npub trait EmailService: Send + Sync {\n    async fn send(&amp;self, to: &amp;str, subject: &amp;str, body: &amp;str) -&gt; Result&lt;(), EmailError&gt;;\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"implementasi-konkret\">Implementasi Konkret\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-rust\">pub struct PgUserRepository {\n    pool: PgPool,\n}\n\n#[async_trait]\nimpl UserRepository for PgUserRepository {\n    async fn find_by_id(&amp;self, id: Uuid) -&gt; Result&lt;Option&lt;User&gt;, DbError&gt; {\n        sqlx::query_as::&lt;_, User&gt;(\"SELECT * FROM users WHERE id = $1\")\n            .bind(id)\n            .fetch_optional(&amp;self.pool)\n            .await\n            .map_err(DbError::from)\n    }\n    \u002F\u002F ... implementasi lainnya\n}\n\npub struct SmtpEmailService {\n    host: String,\n    port: u16,\n}\n\n#[async_trait]\nimpl EmailService for SmtpEmailService {\n    async fn send(&amp;self, to: &amp;str, subject: &amp;str, body: &amp;str) -&gt; Result&lt;(), EmailError&gt; {\n        \u002F\u002F Kirim email via SMTP\n        Ok(())\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"service-layer-dengan-di\">Service Layer dengan DI\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-rust\">pub struct UserService&lt;R: UserRepository, E: EmailService&gt; {\n    repo: R,\n    email: E,\n}\n\nimpl&lt;R: UserRepository, E: EmailService&gt; UserService&lt;R, E&gt; {\n    pub fn new(repo: R, email: E) -&gt; Self {\n        Self { repo, email }\n    }\n    \n    pub async fn register(&amp;self, data: CreateUser) -&gt; Result&lt;User, AppError&gt; {\n        let user = self.repo.create(data).await?;\n        self.email.send(\n            &amp;user.email,\n            \"Selamat datang!\",\n            \"Terima kasih telah mendaftar.\"\n        ).await?;\n        Ok(user)\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"arc-untuk-shared-ownership\">Arc untuk Shared Ownership\u003C\u002Fh2>\n\u003Cp>Dalam aplikasi web, service sering di-share antar request handler. \u003Ccode>Arc\u003C\u002Fcode> memungkinkan shared ownership yang thread-safe:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-rust\">type DynUserRepo = Arc&lt;dyn UserRepository&gt;;\ntype DynEmailService = Arc&lt;dyn EmailService&gt;;\n\n#[derive(Clone)]\npub struct AppState {\n    pub user_repo: DynUserRepo,\n    pub email_svc: DynEmailService,\n}\n\nimpl AppState {\n    pub fn new(pool: PgPool, smtp_config: SmtpConfig) -&gt; Self {\n        Self {\n            user_repo: Arc::new(PgUserRepository { pool: pool.clone() }),\n            email_svc: Arc::new(SmtpEmailService {\n                host: smtp_config.host,\n                port: smtp_config.port,\n            }),\n        }\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"impl-trait-vs-dyn-trait\">impl Trait vs dyn Trait\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>impl Trait (static dispatch):\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-rust\">fn process(repo: &amp;impl UserRepository) { ... }\n\u002F\u002F Dikompilasi menjadi fungsi terpisah per tipe konkret\n\u002F\u002F Zero-cost abstraction, tapi tidak bisa menyimpan tipe berbeda\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>dyn Trait (dynamic dispatch):\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-rust\">fn process(repo: &amp;dyn UserRepository) { ... }\n\u002F\u002F Menggunakan vtable untuk dispatch saat runtime\n\u002F\u002F Fleksibel, overhead minimal (~2ns per panggilan)\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Gunakan \u003Ccode>dyn Trait\u003C\u002Fcode> ketika:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Menyimpan di struct sebagai field\u003C\u002Fli>\n\u003Cli>Menyimpan di koleksi heterogen\u003C\u002Fli>\n\u003Cli>Memerlukan dependency injection saat runtime\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>Gunakan \u003Ccode>impl Trait\u003C\u002Fcode> ketika:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>Parameter fungsi dengan tipe yang diketahui saat kompilasi\u003C\u002Fli>\n\u003Cli>Return type fungsi\u003C\u002Fli>\n\u003Cli>Performa kritis (menghindari vtable lookup)\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Ch2 id=\"testing-dengan-mock\">Testing dengan Mock\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-rust\">#[cfg(test)]\nmod tests {\n    use super::*;\n    \n    struct MockUserRepo {\n        users: Mutex&lt;Vec&lt;User&gt;&gt;,\n    }\n    \n    #[async_trait]\n    impl UserRepository for MockUserRepo {\n        async fn find_by_id(&amp;self, id: Uuid) -&gt; Result&lt;Option&lt;User&gt;, DbError&gt; {\n            let users = self.users.lock().unwrap();\n            Ok(users.iter().find(|u| u.id == id).cloned())\n        }\n        async fn create(&amp;self, data: CreateUser) -&gt; Result&lt;User, DbError&gt; {\n            let user = User {\n                id: Uuid::new_v4(),\n                email: data.email,\n                name: data.name,\n            };\n            self.users.lock().unwrap().push(user.clone());\n            Ok(user)\n        }\n        \u002F\u002F ...\n    }\n    \n    struct MockEmailService {\n        sent: Mutex&lt;Vec&lt;(String, String)&gt;&gt;,\n    }\n    \n    #[async_trait]\n    impl EmailService for MockEmailService {\n        async fn send(&amp;self, to: &amp;str, subject: &amp;str, _body: &amp;str) -&gt; Result&lt;(), EmailError&gt; {\n            self.sent.lock().unwrap().push((to.to_string(), subject.to_string()));\n            Ok(())\n        }\n    }\n    \n    #[tokio::test]\n    async fn test_register_sends_email() {\n        let repo = MockUserRepo { users: Mutex::new(vec![]) };\n        let email = MockEmailService { sent: Mutex::new(vec![]) };\n        let service = UserService::new(repo, email);\n        \n        let user = service.register(CreateUser {\n            email: \"test@example.com\".into(),\n            name: \"Test User\".into(),\n        }).await.unwrap();\n        \n        assert_eq!(user.email, \"test@example.com\");\n        \u002F\u002F Verifikasi email dikirim\n        let sent = service.email.sent.lock().unwrap();\n        assert_eq!(sent.len(), 1);\n        assert_eq!(sent[0].0, \"test@example.com\");\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"kesimpulan\">Kesimpulan\u003C\u002Fh2>\n\u003Cp>Dependency injection di Rust menggunakan trait sebagai interface, Arc untuk shared ownership, dan dyn Trait untuk dispatch runtime. Pola ini memberikan testability tinggi tanpa framework DI eksternal — memanfaatkan kekuatan sistem tipe Rust untuk keamanan kompilasi dan zero-cost abstraction.\u003C\u002Fp>\n","id","b0000000-0000-0000-0000-000000000001",true,"2026-03-28T10:44:25.134418Z","Pola DI di Rust: trait interface, Arc shared ownership, impl vs dyn Trait, dan testing dengan mock untuk service layer.","dependency injection Rust",null,"index, follow",[21,26],{"id":22,"name":23,"slug":24,"created_at":25},"c0000000-0000-0000-0000-000000000022","Performance","performance","2026-03-28T10:44:21.513630Z",{"id":27,"name":28,"slug":29,"created_at":25},"c0000000-0000-0000-0000-000000000001","Rust","rust","Rekayasa",[32,39,46],{"id":33,"title":34,"slug":35,"excerpt":36,"locale":12,"category_name":37,"published_at":38},"d0000000-0000-0000-0000-000000000642","WASI 0.3 dan Kematian Cold Start: Wasm Sisi Server di Produksi","wasi-0-3-kematian-cold-start-wasm-sisi-server-di-produksi","WASI 0.3 dirilis pada Februari 2026 dengan async I\u002FO native, tipe stream, dan dukungan socket penuh. WebAssembly sisi server kini menghadirkan cold start dalam hitungan mikrodetik, dan setiap penyedia cloud besar menawarkan Wasm serverless.","DevOps","2026-03-28T10:44:47.445780Z",{"id":40,"title":41,"slug":42,"excerpt":43,"locale":12,"category_name":44,"published_at":45},"d0000000-0000-0000-0000-000000000620","Stack Backend Modern 2026: Rust + PostgreSQL 18 + Wasm + eBPF","stack-backend-modern-2026-rust-postgresql-wasm-ebpf","Empat teknologi konvergen untuk mendefinisikan ulang infrastruktur backend di 2026: Rust menghilangkan overhead garbage collection dan mengurangi jumlah container hingga 3x, PostgreSQL 18 menggantikan database khusus, WASI 0.3 memberikan cold start mikrodetik untuk fungsi serverless, dan eBPF memungkinkan observabilitas tanpa instrumentasi dengan biaya yang jauh lebih rendah dari monitoring tradisional.","Engineering","2026-03-28T10:44:45.804120Z",{"id":47,"title":48,"slug":49,"excerpt":50,"locale":12,"category_name":30,"published_at":51},"d0000000-0000-0000-0000-000000000548","Dari Autocomplete ke Otonom: Evolusi Alat AI Coding (2022-2026)","dari-autocomplete-ke-otonom-evolusi-alat-ai-coding-2022-2026","Kronik tentang bagaimana alat AI coding berevolusi dari autocomplete satu baris di 2022 menjadi agen multi-file otonom di 2026. Empat tahun yang mengubah pengembangan perangkat lunak selamanya, dengan pandangan tentang apa yang akan datang.","2026-03-28T10:44:41.351824Z",{"id":13,"name":53,"slug":54,"bio":55,"photo_url":18,"linkedin":18,"role":56,"created_at":57,"updated_at":57},"Open Soft Team","open-soft-team","The engineering team at Open Soft, building premium software solutions from Bali, Indonesia.","Engineering Team","2026-03-28T08:31:22.226811Z"]