Rust के लिए Dependency Injection कंटेनर: dicy लाइब्रेरी की समीक्षा
सिस्टम प्रोग्रामिंग की दुनिया में, Rust अपने प्रदर्शन और मेमोरी सुरक्षा के लिए प्रसिद्ध है। हालाँकि, बड़े अनुप्रयोगों के निर्माण करते समय, डेवलपर्स अक्सर निर्भरता प्रबंधन की समस्या का सामना करते हैं। घटकों का मैन्युअल जुड़ाव 'कठोर कोड' की ओर ले जाता है, परीक्षण को जटिल बनाता है और लचीलापन कम करता है। समाधान है Dependency Injection (DI) कंटेनर, जो निर्भरताओं के निर्माण और इंजेक्शन को स्वचालित करता है। इस लेख में, हम dicy लाइब्रेरी की समीक्षा करेंगे - जो Rust के लिए सबसे परिपक्व DI कंटेनरों में से एक है।
Dependency Injection क्या है और Rust में इसकी आवश्यकता क्यों है?
Dependency Injection एक पैटर्न है जिसमें कोई ऑब्जेक्ट अपनी निर्भरताएँ बाहर से प्राप्त करता है, न कि स्वयं बनाता है। Rust में, सख्त टाइप सिस्टम और स्वामित्व (ownership) के कारण यह विशेष रूप से प्रासंगिक है। DI कंटेनर निम्नलिखित कार्य करता है:
- सेवाओं के इंस्टेंस बनाना (singleton, transient, scoped)।
- निर्भरता ग्राफ को हल करना (पुनरावर्ती इंजेक्शन)।
- ऑब्जेक्ट्स के जीवनकाल का प्रबंधन करना।
- परीक्षण के दौरान कार्यान्वयन को बदलने की क्षमता (mock-ऑब्जेक्ट्स)।
dicy लाइब्रेरी: सुविधाओं की समीक्षा
dicy एक हल्का लेकिन शक्तिशाली DI कंटेनर है, जो बिना unsafe कोड के शुद्ध Rust में लिखा गया है। यह समर्थन करता है:
- स्वचालित निर्भरता समाधान कंस्ट्रक्टर या फैक्ट्रियों के माध्यम से।
- जीवनकाल के कई मोड: Singleton, Transient, Scoped (actix-web, axum और अन्य फ्रेमवर्क के लिए)।
- एसिंक्रोनस फैक्ट्रियाँ - async/await का उपयोग करके सेवाएँ बनाना।
- ट्रेट्स के माध्यम से इंजेक्शन - एक ट्रेट को एक विशिष्ट कार्यान्वयन से बांधना।
- नेस्टेड कंटेनर (मॉड्यूल को अलग करने के लिए)।
स्थापना और बुनियादी सेटअप
Cargo.toml में dicy जोड़ें:
[dependencies]dicy = "0.6"उदाहरण 1: कंस्ट्रक्टर के माध्यम से सरल इंजेक्शन
दो सेवाएँ बनाते हैं: Config (singleton) और Database (transient), जो Config पर निर्भर है।
use dicy::{Container, Module, Provide, Singleton, Transient};
#[derive(Clone)]struct Config { url: String,}
impl Config { fn new() -> Self { Config { url: "postgres://localhost".into() } }}
struct Database { config: Config,}
impl Database { fn new(config: Config) -> Self { Database { config } }}
// पंजीकरण के साथ मॉड्यूल परिभाषित करनाstruct AppModule;
impl Module for AppModule { fn configure(container: &mut Container) { container .register::<Config>(Singleton::new(Config::new)) .register::<Database>(Transient::new(Database::new)); }}
fn main() { let container = Container::new(); container.install(AppModule);
// स्वचालित रूप से इंजेक्ट किए गए Config के साथ Database प्राप्त करें let db: Database = container.resolve().unwrap(); println!("DB कनेक्शन: {}", db.config.url);}उदाहरण 2: एसिंक्रोनस फैक्ट्रियाँ और Scoped-निर्भरताएँ
वेब अनुप्रयोगों के लिए, अक्सर HTTP अनुरोध (scoped) के दायरे वाली सेवाएँ बनाने की आवश्यकता होती है। dicy इसका समर्थन करता है:
use dicy::{Container, Module, Scoped, AsyncFactory, Provide};use async_trait::async_trait;
struct RequestContext { user_id: u64,}
#[async_trait]trait UserService { async fn get_name(&self) -> String;}
struct UserServiceImpl { ctx: RequestContext,}
#[async_trait]impl UserService for UserServiceImpl { async fn get_name(&self) -> String { format!("User #{}", self.ctx.user_id) }}
struct WebModule;
impl Module for WebModule { fn configure(container: &mut Container) { container