مقدمه

در درس قبل دیدیم که Crateها و Packageها به ما کمک می‌کنند تا کدهایمان را در سطح کلان سازماندهی کنیم. اما برای یک Crate بزرگ، نیاز به یک سطح دیگر از سازماندهی در داخل خود Crate داریم. اینجاست که سیستم ماژول Rust وارد عمل می‌شود. ماژول‌ها به ما اجازه می‌دهند تا کدها را بر اساس عملکردشان گروه‌بندی کرده، آنها را در یک ساختار درختی مرتب کنیم و مهم‌تر از همه، حریم خصوصی آنها را کنترل کنیم؛ یعنی مشخص کنیم کدام بخش از کدها جزئیات پیاده‌سازی داخلی و خصوصی هستند و کدام بخش‌ها یک رابط عمومی (Public API) برای استفاده توسط کدهای دیگر فراهم می‌کنند.

تعریف ماژول‌ها برای کنترل حوزه و حریم خصوصی

ما با استفاده از کلمه کلیدی mod یک ماژول جدید تعریف می‌کنیم. هر Crate به صورت ضمنی یک ماژول ریشه (root module) دارد که محتوای فایل ریشه (src/main.rs یا src/lib.rs) را در بر می‌گیرد. ما می‌توانیم در داخل این ماژول ریشه، ماژول‌های دیگری تعریف کنیم. ماژول‌ها همچنین می‌توانند به صورت تودرتو تعریف شوند و یک «درخت ماژول» (module tree) را تشکیل دهند.

برای درک بهتر، بیایید یک Crate کتابخانه‌ای برای یک رستوران را تصور کنیم.

Copy Icon src/lib.rs
mod front_of_house {
    mod hosting {
        fn add_to_waitlist() {}

        fn seat_at_table() {}
    }

    mod serving {
        fn take_order() {}

        fn serve_order() {}

        fn take_payment() {}
    }
}

این کد یک درخت ماژول به شکل زیر ایجاد می‌کند:

crate
 └── front_of_house
     ├── hosting
     │   ├── add_to_waitlist
     │   └── seat_at_table
     └── serving
         ├── take_order
         ├── serve_order
         └── take_payment
                    

مسیرها و کلمه کلیدی pub

برای ارجاع به یک آیتم (مانند یک تابع) در درخت ماژول، از یک «مسیر» (path) استفاده می‌کنیم. مسیرها می‌توانند مطلق یا نسبی باشند.

  • مسیرهای مطلق (Absolute Paths): از ریشه Crate و با استفاده از کلمه کلیدی crate شروع می‌شوند.
  • مسیرهای نسبی (Relative Paths): از ماژول فعلی شروع شده و از کلمات کلیدی self (برای خود ماژول) یا super (برای ماژول والد) استفاده می‌کنند.

اما مهم‌تر از مسیر، «حریم خصوصی» (privacy) است. در Rust، تمام آیتم‌ها (توابع، structها، enumها، ماژول‌ها و...) به صورت پیش‌فرض خصوصی هستند. این یعنی آنها فقط در ماژول والد خود و ماژول‌های فرزند آن قابل دسترسی‌اند. برای اینکه یک آیتم از خارج از ماژول والد خود قابل دسترس باشد، باید آن را با کلمه کلیدی pub (مخفف public) علامت‌گذاری کنیم.

Copy Icon src/lib.rs
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

pub fn eat_at_restaurant() {
    // Absolute path from crate root
    crate::front_of_house::hosting::add_to_waitlist();
}

در این کد، برای اینکه بتوانیم تابع add_to_waitlist را از داخل تابع eat_at_restaurant (که در ماژول ریشه قرار دارد) فراخوانی کنیم، باید هم ماژول والد آن (hosting) و هم خود تابع را pub کنیم.

ایجاد میانبر با کلمه کلیدی use

نوشتن مسیرهای طولانی به صورت مکرر، خسته‌کننده و پر از تکرار است. کلمه کلیدی use به ما اجازه می‌دهد تا یک مسیر را به حوزه فعلی "وارد" کرده و یک میانبر برای آن ایجاد کنیم. پس از آن، می‌توانیم از نام نهایی مسیر برای ارجاع به آن آیتم استفاده کنیم.

یک قرارداد رایج در Rust این است که برای توابع، ماژول والد آنها را use می‌کنیم، اما برای structها، enumها و دیگر آیتم‌ها، مسیر کامل تا خود آیتم را use می‌کنیم.

Copy Icon src/lib.rs
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

// Bring the `hosting` module into the current scope.
use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
    // Now we can use the shorter path.
    hosting::add_to_waitlist();
    hosting::add_to_waitlist();
}

در این نسخه بهبودیافته، ما ماژول hosting را به حوزه ماژول ریشه وارد کرده‌ایم و دیگر نیازی به تکرار مسیر کامل crate::front_of_house::hosting نیست. این کار کد را بسیار خواناتر می‌کند.

در این درس با سیستم ماژول Rust و نحوه استفاده از آن برای سازماندهی کد، گروه‌بندی آیتم‌های مرتبط و مدیریت حریم خصوصی آنها با کلمه کلیدی pub آشنا شدیم. همچنین دیدیم که چگونه می‌توان با استفاده از مسیرها و کلمه کلیدی use به آیتم‌های داخل ماژول‌ها دسترسی پیدا کرد. در درس بعدی، به بررسی دقیق‌تر نحوه کار با مسیرها و الگوهای پیشرفته‌تر آن خواهیم پرداخت.