[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"article-deep-evm-22-dependency-injection-rust-service-locator-ru":3},{"article":4,"author":51},{"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":16,"meta_description":17,"focus_keyword":18,"og_image":19,"canonical_url":19,"robots_meta":20,"created_at":15,"updated_at":15,"tags":21,"category_name":31,"related_articles":32},"d0000000-0000-0000-0000-000000000222","a0000000-0000-0000-0000-000000000016","Deep EVM #22: Dependency Injection в Rust — ServiceLocator, Arc и трейт-объекты","deep-evm-22-dependency-injection-rust-service-locator-ru","Паттерны внедрения зависимостей в Rust: от трейт-объектов и Arc до ServiceLocator и compile-time DI с дженериками.","## DI в Rust: другой подход\n\nDependency Injection (DI) в Rust выглядит иначе, чем в Java или C#. Здесь нет рефлексии, нет аннотаций `@Inject`, нет DI-контейнеров в привычном смысле. Вместо этого Rust предлагает мощную систему трейтов, дженериков и lifetime'ов, которая позволяет реализовать DI на уровне типов — с нулевыми runtime-затратами.\n\nВ этой статье мы рассмотрим четыре подхода к DI в Rust: трейт-объекты, дженерики, паттерн ServiceLocator и compile-time DI.\n\n## Подход 1: Трейт-объекты и Arc\n\nСамый распространённый подход — определить трейт для каждой зависимости и передавать `Arc\u003Cdyn Trait>`:\n\n```rust\nuse std::sync::Arc;\n\n#[async_trait::async_trait]\ntrait UserRepository: Send + Sync {\n    async fn find_by_id(&self, id: Uuid) -> Result\u003CUser>;\n    async fn save(&self, user: &User) -> Result\u003C()>;\n}\n\n#[async_trait::async_trait]\ntrait EmailService: Send + Sync {\n    async fn send(&self, to: &str, subject: &str, body: &str) -> Result\u003C()>;\n}\n\nstruct UserService {\n    repo: Arc\u003Cdyn UserRepository>,\n    email: Arc\u003Cdyn EmailService>,\n}\n\nimpl UserService {\n    fn new(\n        repo: Arc\u003Cdyn UserRepository>,\n        email: Arc\u003Cdyn EmailService>,\n    ) -> Self {\n        Self { repo, email }\n    }\n\n    async fn register(&self, name: String, email_addr: String) -> Result\u003CUser> {\n        let user = User::new(name, email_addr.clone());\n        self.repo.save(&user).await?;\n        self.email.send(&email_addr, \"Welcome\", \"Hello!\").await?;\n        Ok(user)\n    }\n}\n```\n\nПлюсы: простота, привычно для разработчиков из других языков.\nМинусы: динамическая диспетчеризация (vtable), аллокация через Arc.\n\n## Подход 2: Дженерики (compile-time DI)\n\nДля максимальной производительности используйте дженерики — компилятор мономорфизирует код:\n\n```rust\nstruct UserService\u003CR, E>\nwhere\n    R: UserRepository,\n    E: EmailService,\n{\n    repo: R,\n    email: E,\n}\n\nimpl\u003CR: UserRepository, E: EmailService> UserService\u003CR, E> {\n    fn new(repo: R, email: E) -> Self {\n        Self { repo, email }\n    }\n}\n```\n\nПлюсы: нулевые runtime-затраты, инлайнинг.\nМинусы: сложные типы, дублирование кода при мономорфизации.\n\n## Подход 3: Паттерн ServiceLocator\n\nServiceLocator — это контейнер зависимостей, из которого компоненты извлекают нужные сервисы:\n\n```rust\nuse std::any::{Any, TypeId};\nuse std::collections::HashMap;\nuse std::sync::Arc;\n\nstruct ServiceLocator {\n    services: HashMap\u003CTypeId, Arc\u003Cdyn Any + Send + Sync>>,\n}\n\nimpl ServiceLocator {\n    fn new() -> Self {\n        Self { services: HashMap::new() }\n    }\n\n    fn register\u003CT: Send + Sync + 'static>(&mut self, service: T) {\n        self.services.insert(\n            TypeId::of::\u003CT>(),\n            Arc::new(service),\n        );\n    }\n\n    fn resolve\u003CT: Send + Sync + 'static>(&self) -> Option\u003CArc\u003CT>> {\n        self.services\n            .get(&TypeId::of::\u003CT>())\n            .and_then(|s| s.clone().downcast::\u003CT>().ok())\n    }\n}\n```\n\nИспользование:\n\n```rust\nlet mut locator = ServiceLocator::new();\nlocator.register(PgUserRepository::new(pool.clone()));\nlocator.register(SmtpEmailService::new(config));\n\nlet repo: Arc\u003CPgUserRepository> = locator.resolve().unwrap();\n```\n\nПлюсы: гибкость, динамическая регистрация.\nМинусы: нет compile-time проверки, downcast может упасть.\n\n## Подход 4: AppState (идиома Axum)\n\nВ Axum идиоматический подход — создать структуру AppState со всеми зависимостями:\n\n```rust\n#[derive(Clone)]\nstruct AppState {\n    db: PgPool,\n    redis: RedisPool,\n    jwt_secret: String,\n    email: Arc\u003Cdyn EmailService>,\n}\n\nasync fn create_user(\n    State(state): State\u003CAppState>,\n    Json(payload): Json\u003CCreateUserRequest>,\n) -> Result\u003CJson\u003CUser>, AppError> {\n    let user = User::new(payload.name);\n    sqlx::query!(\"INSERT INTO users ...\")\n        .execute(&state.db)\n        .await?;\n    state.email.send(&user.email, \"Welcome\", \"Hello!\").await?;\n    Ok(Json(user))\n}\n```\n\nЭто самый прагматичный подход для веб-приложений на Axum.\n\n## Тестирование с DI\n\nГлавное преимущество DI — тестируемость. Создавайте mock-реализации трейтов:\n\n```rust\n#[cfg(test)]\nmod tests {\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\u003CUser> {\n            self.users.lock().unwrap()\n                .iter()\n                .find(|u| u.id == id)\n                .cloned()\n                .ok_or(Error::NotFound)\n        }\n\n        async fn save(&self, user: &User) -> Result\u003C()> {\n            self.users.lock().unwrap().push(user.clone());\n            Ok(())\n        }\n    }\n\n    #[tokio::test]\n    async fn test_register_user() {\n        let repo = Arc::new(MockUserRepo::default());\n        let email = Arc::new(MockEmailService::default());\n        let service = UserService::new(repo.clone(), email);\n\n        let user = service.register(\"Alice\".into(), \"a@b.com\".into())\n            .await.unwrap();\n\n        assert_eq!(user.name, \"Alice\");\n    }\n}\n```\n\n## Выбор подхода\n\n| Подход | Runtime-затраты | Тестируемость | Сложность |\n|--------|-----------------|---------------|-----------|\n| Трейт-объекты | Низкие (vtable) | Высокая | Средняя |\n| Дженерики | Нулевые | Высокая | Высокая |\n| ServiceLocator | Средние | Средняя | Низкая |\n| AppState | Низкие | Высокая | Низкая |\n\nДля большинства веб-приложений на Axum рекомендуем AppState + трейт-объекты для сервисов, которые нужно подменять в тестах.\n\n## Заключение\n\nDI в Rust — это не фреймворк, а набор паттернов. Система типов Rust обеспечивает безопасность на этапе компиляции, что делает многие DI-фреймворки просто ненужными. Выбирайте подход исходя из требований проекта: для максимальной производительности — дженерики, для гибкости — трейт-объекты, для простоты — AppState.","\u003Ch2 id=\"di-rust\">DI в Rust: другой подход\u003C\u002Fh2>\n\u003Cp>Dependency Injection (DI) в Rust выглядит иначе, чем в Java или C#. Здесь нет рефлексии, нет аннотаций \u003Ccode>@Inject\u003C\u002Fcode>, нет DI-контейнеров в привычном смысле. Вместо этого Rust предлагает мощную систему трейтов, дженериков и lifetime’ов, которая позволяет реализовать DI на уровне типов — с нулевыми runtime-затратами.\u003C\u002Fp>\n\u003Cp>В этой статье мы рассмотрим четыре подхода к DI в Rust: трейт-объекты, дженерики, паттерн ServiceLocator и compile-time DI.\u003C\u002Fp>\n\u003Ch2 id=\"1-arc\">Подход 1: Трейт-объекты и Arc\u003C\u002Fh2>\n\u003Cp>Самый распространённый подход — определить трейт для каждой зависимости и передавать \u003Ccode>Arc&lt;dyn Trait&gt;\u003C\u002Fcode>:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-rust\">use std::sync::Arc;\n\n#[async_trait::async_trait]\ntrait UserRepository: Send + Sync {\n    async fn find_by_id(&amp;self, id: Uuid) -&gt; Result&lt;User&gt;;\n    async fn save(&amp;self, user: &amp;User) -&gt; Result&lt;()&gt;;\n}\n\n#[async_trait::async_trait]\ntrait EmailService: Send + Sync {\n    async fn send(&amp;self, to: &amp;str, subject: &amp;str, body: &amp;str) -&gt; Result&lt;()&gt;;\n}\n\nstruct UserService {\n    repo: Arc&lt;dyn UserRepository&gt;,\n    email: Arc&lt;dyn EmailService&gt;,\n}\n\nimpl UserService {\n    fn new(\n        repo: Arc&lt;dyn UserRepository&gt;,\n        email: Arc&lt;dyn EmailService&gt;,\n    ) -&gt; Self {\n        Self { repo, email }\n    }\n\n    async fn register(&amp;self, name: String, email_addr: String) -&gt; Result&lt;User&gt; {\n        let user = User::new(name, email_addr.clone());\n        self.repo.save(&amp;user).await?;\n        self.email.send(&amp;email_addr, \"Welcome\", \"Hello!\").await?;\n        Ok(user)\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Плюсы: простота, привычно для разработчиков из других языков.\nМинусы: динамическая диспетчеризация (vtable), аллокация через Arc.\u003C\u002Fp>\n\u003Ch2 id=\"2-compile-time-di\">Подход 2: Дженерики (compile-time DI)\u003C\u002Fh2>\n\u003Cp>Для максимальной производительности используйте дженерики — компилятор мономорфизирует код:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-rust\">struct UserService&lt;R, E&gt;\nwhere\n    R: UserRepository,\n    E: EmailService,\n{\n    repo: R,\n    email: E,\n}\n\nimpl&lt;R: UserRepository, E: EmailService&gt; UserService&lt;R, E&gt; {\n    fn new(repo: R, email: E) -&gt; Self {\n        Self { repo, email }\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Плюсы: нулевые runtime-затраты, инлайнинг.\nМинусы: сложные типы, дублирование кода при мономорфизации.\u003C\u002Fp>\n\u003Ch2 id=\"3-servicelocator\">Подход 3: Паттерн ServiceLocator\u003C\u002Fh2>\n\u003Cp>ServiceLocator — это контейнер зависимостей, из которого компоненты извлекают нужные сервисы:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-rust\">use std::any::{Any, TypeId};\nuse std::collections::HashMap;\nuse std::sync::Arc;\n\nstruct ServiceLocator {\n    services: HashMap&lt;TypeId, Arc&lt;dyn Any + Send + Sync&gt;&gt;,\n}\n\nimpl ServiceLocator {\n    fn new() -&gt; Self {\n        Self { services: HashMap::new() }\n    }\n\n    fn register&lt;T: Send + Sync + 'static&gt;(&amp;mut self, service: T) {\n        self.services.insert(\n            TypeId::of::&lt;T&gt;(),\n            Arc::new(service),\n        );\n    }\n\n    fn resolve&lt;T: Send + Sync + 'static&gt;(&amp;self) -&gt; Option&lt;Arc&lt;T&gt;&gt; {\n        self.services\n            .get(&amp;TypeId::of::&lt;T&gt;())\n            .and_then(|s| s.clone().downcast::&lt;T&gt;().ok())\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Использование:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-rust\">let mut locator = ServiceLocator::new();\nlocator.register(PgUserRepository::new(pool.clone()));\nlocator.register(SmtpEmailService::new(config));\n\nlet repo: Arc&lt;PgUserRepository&gt; = locator.resolve().unwrap();\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Плюсы: гибкость, динамическая регистрация.\nМинусы: нет compile-time проверки, downcast может упасть.\u003C\u002Fp>\n\u003Ch2 id=\"4-appstate-axum\">Подход 4: AppState (идиома Axum)\u003C\u002Fh2>\n\u003Cp>В Axum идиоматический подход — создать структуру AppState со всеми зависимостями:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-rust\">#[derive(Clone)]\nstruct AppState {\n    db: PgPool,\n    redis: RedisPool,\n    jwt_secret: String,\n    email: Arc&lt;dyn EmailService&gt;,\n}\n\nasync fn create_user(\n    State(state): State&lt;AppState&gt;,\n    Json(payload): Json&lt;CreateUserRequest&gt;,\n) -&gt; Result&lt;Json&lt;User&gt;, AppError&gt; {\n    let user = User::new(payload.name);\n    sqlx::query!(\"INSERT INTO users ...\")\n        .execute(&amp;state.db)\n        .await?;\n    state.email.send(&amp;user.email, \"Welcome\", \"Hello!\").await?;\n    Ok(Json(user))\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>Это самый прагматичный подход для веб-приложений на Axum.\u003C\u002Fp>\n\u003Ch2 id=\"di\">Тестирование с DI\u003C\u002Fh2>\n\u003Cp>Главное преимущество DI — тестируемость. Создавайте mock-реализации трейтов:\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-rust\">#[cfg(test)]\nmod tests {\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;User&gt; {\n            self.users.lock().unwrap()\n                .iter()\n                .find(|u| u.id == id)\n                .cloned()\n                .ok_or(Error::NotFound)\n        }\n\n        async fn save(&amp;self, user: &amp;User) -&gt; Result&lt;()&gt; {\n            self.users.lock().unwrap().push(user.clone());\n            Ok(())\n        }\n    }\n\n    #[tokio::test]\n    async fn test_register_user() {\n        let repo = Arc::new(MockUserRepo::default());\n        let email = Arc::new(MockEmailService::default());\n        let service = UserService::new(repo.clone(), email);\n\n        let user = service.register(\"Alice\".into(), \"a@b.com\".into())\n            .await.unwrap();\n\n        assert_eq!(user.name, \"Alice\");\n    }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"\">Выбор подхода\u003C\u002Fh2>\n\u003Ctable>\u003Cthead>\u003Ctr>\u003Cth>Подход\u003C\u002Fth>\u003Cth>Runtime-затраты\u003C\u002Fth>\u003Cth>Тестируемость\u003C\u002Fth>\u003Cth>Сложность\u003C\u002Fth>\u003C\u002Ftr>\u003C\u002Fthead>\u003Ctbody>\n\u003Ctr>\u003Ctd>Трейт-объекты\u003C\u002Ftd>\u003Ctd>Низкие (vtable)\u003C\u002Ftd>\u003Ctd>Высокая\u003C\u002Ftd>\u003Ctd>Средняя\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>Дженерики\u003C\u002Ftd>\u003Ctd>Нулевые\u003C\u002Ftd>\u003Ctd>Высокая\u003C\u002Ftd>\u003Ctd>Высокая\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>ServiceLocator\u003C\u002Ftd>\u003Ctd>Средние\u003C\u002Ftd>\u003Ctd>Средняя\u003C\u002Ftd>\u003Ctd>Низкая\u003C\u002Ftd>\u003C\u002Ftr>\n\u003Ctr>\u003Ctd>AppState\u003C\u002Ftd>\u003Ctd>Низкие\u003C\u002Ftd>\u003Ctd>Высокая\u003C\u002Ftd>\u003Ctd>Низкая\u003C\u002Ftd>\u003C\u002Ftr>\n\u003C\u002Ftbody>\u003C\u002Ftable>\n\u003Cp>Для большинства веб-приложений на Axum рекомендуем AppState + трейт-объекты для сервисов, которые нужно подменять в тестах.\u003C\u002Fp>\n\u003Ch2 id=\"\">Заключение\u003C\u002Fh2>\n\u003Cp>DI в Rust — это не фреймворк, а набор паттернов. Система типов Rust обеспечивает безопасность на этапе компиляции, что делает многие DI-фреймворки просто ненужными. Выбирайте подход исходя из требований проекта: для максимальной производительности — дженерики, для гибкости — трейт-объекты, для простоты — AppState.\u003C\u002Fp>\n","ru","b0000000-0000-0000-0000-000000000001",true,"2026-03-28T10:44:23.932946Z","Dependency Injection в Rust — ServiceLocator, Arc и трейт-объекты","Паттерны DI в Rust: трейт-объекты, дженерики, ServiceLocator и AppState для Axum-приложений.","dependency injection rust",null,"index, follow",[22,27],{"id":23,"name":24,"slug":25,"created_at":26},"c0000000-0000-0000-0000-000000000022","Performance","performance","2026-03-28T10:44:21.513630Z",{"id":28,"name":29,"slug":30,"created_at":26},"c0000000-0000-0000-0000-000000000001","Rust","rust","Инженерия",[33,39,45],{"id":34,"title":35,"slug":36,"excerpt":37,"locale":12,"category_name":31,"published_at":38},"d0200000-0000-0000-0000-000000000013","Почему Бали становится хабом импакт-технологий Юго-Восточной Азии в 2026 году","pochemu-bali-stanovitsya-khabom-impakt-tekhnologiy-2026","Бали занимает 16-е место среди стартап-экосистем Юго-Восточной Азии. Растущая концентрация Web3-разработчиков, ИИ-стартапов в области устойчивого развития и компаний в сфере эко-тревел-технологий формирует нишу столицы импакт-технологий региона.","2026-03-28T10:44:37.953039Z",{"id":40,"title":41,"slug":42,"excerpt":43,"locale":12,"category_name":31,"published_at":44},"d0200000-0000-0000-0000-000000000012","Защита данных в ASEAN: чек-лист разработчика для мультистранового комплаенса","zashchita-dannykh-asean-chek-list-razrabotchika-komplaens","Семь стран ASEAN имеют собственные законы о защите данных с разными моделями согласия, требованиями к локализации и штрафами. Практический чек-лист для разработчиков мультистрановых приложений.","2026-03-28T10:44:37.944001Z",{"id":46,"title":47,"slug":48,"excerpt":49,"locale":12,"category_name":31,"published_at":50},"d0200000-0000-0000-0000-000000000011","Цифровая трансформация Индонезии на $29 миллиардов: возможности для софтверных компаний","tsifrovaya-transformatsiya-indonezii-29-milliardov-vozmozhnosti-dlya-kompaniy","Рынок IT-услуг Индонезии вырастет с $24,37 млрд в 2025 году до $29,03 млрд в 2026 году. Облачная инфраструктура, искусственный интеллект, электронная коммерция и дата-центры обеспечивают самый быстрый рост в Юго-Восточной Азии.","2026-03-28T10:44:37.917095Z",{"id":13,"name":52,"slug":53,"bio":54,"photo_url":19,"linkedin":19,"role":55,"created_at":56,"updated_at":56},"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"]