تعریف ماژولها برای کنترل محدوده و حریم خصوصی
در این بخش، ما درباره ماژولها و سایر بخشهای سیستم ماژول صحبت خواهیم کرد، یعنی مسیرها که به شما امکان میدهند آیتمها را نامگذاری کنید؛ کلمه کلیدی use
که مسیر را به محدوده وارد میکند؛ و کلمه کلیدی pub
برای عمومی کردن آیتمها. همچنین درباره کلمه کلیدی as
، بستههای خارجی، و عملگر glob
صحبت خواهیم کرد.
خلاصهای از ماژولها
قبل از اینکه به جزئیات ماژولها و مسیرها بپردازیم، اینجا یک مرجع سریع در مورد نحوه عملکرد ماژولها، مسیرها، کلمه کلیدی use
و کلمه کلیدی pub
در کامپایلر ارائه میدهیم و همچنین نحوه سازماندهی کد توسط اکثر توسعهدهندگان را نشان میدهیم. ما در طول این فصل به مثالهایی از هر یک از این قواعد خواهیم پرداخت، اما این یک مکان عالی برای یادآوری نحوه عملکرد ماژولها است.
- شروع از ریشه جعبه (crate): هنگام کامپایل یک جعبه (crate)، کامپایلر ابتدا در فایل ریشه جعبه (crate) (معمولاً src/lib.rs برای یک جعبه (crate) کتابخانهای یا src/main.rs برای یک جعبه (crate) باینری) به دنبال کد برای کامپایل میگردد.
- تعریف ماژولها: در فایل ریشه جعبه (crate)، میتوانید ماژولهای جدید تعریف کنید؛ مثلاً میتوانید یک ماژول “garden” با
mod garden;
تعریف کنید. کامپایلر کد ماژول را در مکانهای زیر جستجو میکند:- به صورت درونخطی، داخل براکتهای موجدار که به جای علامت نقطهویرگول بعد از
mod garden
قرار میگیرند. - در فایل src/garden.rs
- در فایل src/garden/mod.rs
- به صورت درونخطی، داخل براکتهای موجدار که به جای علامت نقطهویرگول بعد از
- تعریف زیرماژولها: در هر فایلی به جز فایل ریشه جعبه (crate)، میتوانید زیرماژولها تعریف کنید. برای مثال، ممکن است
mod vegetables;
را در فایل src/garden.rs تعریف کنید. کامپایلر کد زیرماژول را در دایرکتوریای که به نام ماژول والد است، در مکانهای زیر جستجو میکند:- به صورت درونخطی، مستقیماً بعد از
mod vegetables
، داخل براکتهای موجدار به جای نقطهویرگول - در فایل src/garden/vegetables.rs
- در فایل src/garden/vegetables/mod.rs
- به صورت درونخطی، مستقیماً بعد از
- مسیرها به کد در ماژولها: وقتی یک ماژول بخشی از جعبه (crate) شما باشد، میتوانید از هر جای دیگر در همان جعبه (crate) (تا زمانی که قواعد حریم خصوصی اجازه دهند) با استفاده از مسیر به کد آن ارجاع دهید. برای مثال، یک نوع
Asparagus
در ماژول vegetables در garden به این صورت پیدا میشود:crate::garden::vegetables::Asparagus
. - خصوصی در مقابل عمومی: کد درون یک ماژول به صورت پیشفرض برای ماژولهای والد خصوصی است. برای عمومی کردن یک ماژول، آن را با
pub mod
به جایmod
تعریف کنید. برای عمومی کردن آیتمهای داخل یک ماژول عمومی، ازpub
قبل از اعلان آنها استفاده کنید. - کلمه کلیدی
use
: در یک محدوده، کلمه کلیدیuse
میانبری به آیتمها ایجاد میکند تا تکرار مسیرهای طولانی کاهش یابد. در هر محدودهای که میتواند بهcrate::garden::vegetables::Asparagus
ارجاع دهد، میتوانید یک میانبر باuse crate::garden::vegetables::Asparagus;
ایجاد کنید و از آن به بعد فقط کافی استAsparagus
را در آن محدوده استفاده کنید.
اینجا، ما یک جعبه (crate) باینری به نام backyard
ایجاد میکنیم که این قواعد را نشان میدهد. دایرکتوری جعبه (crate) که آن هم backyard
نامیده میشود شامل این فایلها و دایرکتوریها است:
backyard
├── Cargo.lock
├── Cargo.toml
└── src
├── garden
│ └── vegetables.rs
├── garden.rs
└── main.rs
فایل ریشه جعبه (crate) در اینجا src/main.rs است و حاوی موارد زیر است:
use crate::garden::vegetables::Asparagus;
pub mod garden;
fn main() {
let plant = Asparagus {};
println!("I'm growing {plant:?}!");
}
خط pub mod garden;
به کامپایلر میگوید که کدی را که در src/garden.rs پیدا میکند وارد کند، که شامل موارد زیر است:
pub mod vegetables;
اینجا، pub mod vegetables;
به این معنا است که کد موجود در src/garden/vegetables.rs نیز وارد میشود. آن کد به صورت زیر است:
#[derive(Debug)]
pub struct Asparagus {}
حالا بیایید به جزئیات این قواعد بپردازیم و آنها را در عمل نشان دهیم!
گروهبندی کدهای مرتبط در ماژولها
ماژولها به ما امکان میدهند کد را در یک جعبه (crate) برای خوانایی و بازاستفاده آسان سازماندهی کنیم. ماژولها همچنین به ما امکان کنترل حریم خصوصی آیتمها را میدهند زیرا کد درون یک ماژول به صورت پیشفرض خصوصی است. آیتمهای خصوصی جزئیات پیادهسازی داخلی هستند که برای استفاده خارجی در دسترس نیستند. ما میتوانیم انتخاب کنیم که ماژولها و آیتمهای درون آنها عمومی باشند، که این موارد را برای استفاده خارجی آشکار میکند.
برای مثال، بیایید یک جعبه (crate) کتابخانهای بنویسیم که عملکرد یک رستوران را ارائه دهد. امضای توابع را تعریف میکنیم اما بدنه آنها را خالی میگذاریم تا بیشتر بر سازماندهی کد تمرکز کنیم تا پیادهسازی عملکرد یک رستوران.
در صنعت رستوران، برخی قسمتهای یک رستوران به عنوان جلوی خانه و دیگر قسمتها به عنوان پشت خانه شناخته میشوند. جلوی خانه جایی است که مشتریان هستند؛ این شامل جایی است که میزبانها مشتریان را مینشانند، گارسونها سفارش میگیرند و پرداختها را انجام میدهند، و بارتندرها نوشیدنی درست میکنند. پشت خانه جایی است که سرآشپزها و آشپزها در آشپزخانه کار میکنند، ظرفشورها ظروف را تمیز میکنند، و مدیران کارهای اداری انجام میدهند.
برای ساختاردهی جعبه (crate) خود به این روش، میتوانیم عملکردها را در ماژولهای تو در تو سازماندهی کنیم. یک کتابخانه جدید به نام restaurant
با اجرای دستور cargo new restaurant --lib
ایجاد کنید. سپس کد لیستینگ 7-1 را در 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() {}
}
}
front_of_house
که شامل ماژولهای دیگر است که سپس شامل توابع میشوندما یک ماژول با کلمه کلیدی mod
و سپس نام ماژول تعریف میکنیم (در این مورد، front_of_house
). بدنه ماژول سپس داخل براکتهای موجدار قرار میگیرد. داخل ماژولها، میتوانیم ماژولهای دیگری قرار دهیم، همانطور که در اینجا با ماژولهای hosting
و serving
انجام دادهایم. ماژولها همچنین میتوانند تعاریف آیتمهای دیگر را نگه دارند، مانند ساختارها، enumها، ثابتها، traits و—همانطور که در لیستینگ 7-1 دیده میشود—توابع.
با استفاده از ماژولها، میتوانیم تعاریف مرتبط را با هم گروهبندی کنیم و دلیل ارتباط آنها را نامگذاری کنیم. برنامهنویسانی که از این کد استفاده میکنند میتوانند بر اساس گروهها کد را مرور کنند، به جای اینکه مجبور باشند تمام تعاریف را بخوانند. این کار پیدا کردن تعاریف مرتبط با آنها را آسانتر میکند. برنامهنویسانی که عملکرد جدیدی به این کد اضافه میکنند میدانند که کد را کجا قرار دهند تا برنامه سازماندهی شده باقی بماند.
درخت ماژول
قبلاً اشاره کردیم که src/main.rs و src/lib.rs به نام ریشه جعبه (crate) شناخته میشوند. دلیل نامگذاری آنها این است که محتوای هر یک از این دو فایل یک ماژول به نام crate
را در ریشه ساختار ماژول جعبه (crate) تشکیل میدهند، که به عنوان درخت ماژول شناخته میشود.
لیستینگ 7-2 درخت ماژول را برای ساختار موجود در لیستینگ 7-1 نشان میدهد.
crate
└── front_of_house
├── hosting
│ ├── add_to_waitlist
│ └── seat_at_table
└── serving
├── take_order
├── serve_order
└── take_payment
این درخت نشان میدهد که برخی از ماژولها در داخل ماژولهای دیگر قرار دارند؛ برای مثال، hosting
در داخل front_of_house
قرار دارد. درخت همچنین نشان میدهد که برخی از ماژولها همسطح هستند، به این معنی که در همان ماژول تعریف شدهاند؛ hosting
و serving
همسطح هستند و درون front_of_house
تعریف شدهاند. اگر ماژول A درون ماژول B قرار گیرد، میگوییم ماژول A فرزند ماژول B است و ماژول B والد ماژول A است. توجه کنید که کل درخت ماژول در زیر ماژول ضمنی به نام crate
ریشه دارد.
درخت ماژول ممکن است شما را به یاد درخت دایرکتوریهای فایلسیستم کامپیوتر بیندازد؛ این مقایسه بسیار مناسبی است! درست همانطور که دایرکتوریها در فایلسیستم کد را سازماندهی میکنند، شما میتوانید از ماژولها برای سازماندهی کد خود استفاده کنید. و درست مانند فایلها در یک دایرکتوری، ما نیاز به روشی برای پیدا کردن ماژولها داریم.