سازماندهی پروژههای بزرگ
به فصل «مدیریت پروژه با پکیجها و ماژولها» خوش آمدید. با افزایش حجم و پیچیدگی یک پروژه،
سازماندهی کد به یکی از مهمترین چالشها تبدیل میشود. نوشتن تمام کدها در یک فایل واحد، به سرعت
غیرقابل مدیریت میشود. زبان Rust یک سیستم قدرتمند برای سازماندهی کد ارائه میدهد که به شما اجازه
میدهد کد خود را به بخشهای منطقی و قابل استفاده مجدد تقسیم کنید. اساس این سیستم بر دو مفهوم
کلیدی استوار است: Crate و Package. درک تفاوت و ارتباط این دو مفهوم برای مدیریت
پروژههای بزرگ در Rust ضروری است.
Crate چیست؟
یک Crate کوچکترین واحد کامپایل در زبان Rust است. وقتی شما کدی را با کامپایلر Rust یعنی
rustc کامپایل میکنید، ورودی این کامپایلر یک یا چند Crate است. کد داخل یک
Crate به صورت یک واحد یکپارچه با هم کامپایل میشود.
Crateها به دو شکل اصلی وجود دارند:
- Crate باینری (Binary Crate): این نوع Crate میتواند به یک فایل اجرایی مستقل کامپایل
شود. یک Crate باینری باید حتماً یک تابع main داشته باشد که نقطه شروع اجرای برنامه
است. طبق قرارداد، فایل src/main.rs «ریشه» (crate root) یک Crate باینری در یک پکیج
است.
- Crate کتابخانهای (Library Crate): این نوع Crate به خودی خود قابل اجرا نیست و تابع
main ندارد. هدف آن ارائه مجموعهای از توابع، نوعها و منطق است که توسط Crateهای دیگر
(چه باینری و چه کتابخانهای) مورد استفاده قرار گیرد. طبق قرارداد، فایل src/lib.rs
ریشه یک Crate کتابخانهای است.
پکیج چیست؟
یک Package یک یا چند Crate را در کنار هم بستهبندی میکند تا یک مجموعه از قابلیتها را
ارائه دهد. یک پکیج توسط فایل مانیفست Cargo.toml تعریف میشود. این فایل حاوی اطلاعاتی
مانند نام پکیج، نسخه، نویسنده و وابستگیهای آن به پکیجهای دیگر است.
Cargo، ابزار مدیریت پروژه و بیلد در Rust، در واقع با پکیجها کار میکند. وقتی شما یک دستور
cargo build را اجرا میکنید، Cargo فایل Cargo.toml را خوانده و Crateهای داخل آن
پکیج را
کامپایل میکند. قوانین مربوط به ساختار یک پکیج به شرح زیر است:
- یک پکیج میتواند حداکثر یک Crate کتابخانهای داشته باشد.
- یک پکیج میتواند هر تعداد Crate باینری داشته باشد.
- یک پکیج باید حداقل یک Crate (از هر نوعی) داشته باشد.
یک پکیج در عمل
بیایید این مفاهیم را با یک مثال عملی بررسی کنیم. وقتی شما با دستور cargo new my_project
یک
پروژه جدید میسازید، Cargo در واقع یک پکیج جدید برای شما ایجاد میکند.
$ cargo new my_project
$ cd my_project
ساختار این پکیج به صورت پیشفرض شامل فایل Cargo.toml و یک پوشه src است که داخل آن
فایل main.rs قرار دارد. این یعنی Cargo یک پکیج ساخته که حاوی یک Crate
باینری است و ریشه آن فایل src/main.rs میباشد.
حالا بیایید یک Crate کتابخانهای به همین پکیج اضافه کنیم. برای این کار، کافیست
یک فایل به نام lib.rs در پوشه src بسازیم.
src/lib.rs
pub fn add_one(x: i32) -> i32 {
x + 1
}
اکنون پکیج ما هم یک Crate باینری و هم یک Crate کتابخانهای دارد. ما میتوانیم از کدهای Crate
کتابخانهای خود در Crate باینری استفاده کنیم. برای این کار، باید از نام پکیج خود (که در
Cargo.toml تعریف شده) برای ارجاع به آن استفاده کنیم.
src/main.rs
use my_project::add_one;
fn main() {
let num = 10;
let answer = add_one(num);
println!("Hello, world! {} plus one is {}!", num, answer);
}
وقتی cargo run را اجرا میکنیم، Cargo ابتدا Crate کتابخانهای (src/lib.rs) را
کامپایل
میکند و سپس Crate باینری (src/main.rs) را کامپایل میکند و در حین این کار، به آن اجازه
میدهد تا از توابع export شده (با کلمه کلیدی pub) در Crate کتابخانهای استفاده
کند.
در این درس با مفاهیم بنیادی پکیج و Crate به عنوان واحدهای اصلی سازماندهی و کامپایل
کد در Rust آشنا شدیم. یک پکیج میتواند شامل یک یا چند Crate باشد که به ما اجازه میدهد منطق قابل
استفاده مجدد را در یک Crate کتابخانهای قرار داده و برنامه اصلی را در یک Crate باینری پیادهسازی
کنیم. اما درون یک Crate، چگونه میتوانیم کد را به بخشهای کوچکتر و مدیریتپذیرتر تقسیم کنیم؟ در
درس بعدی، به «نقش ماژولها» خواهیم پرداخت و یاد میگیریم که چگونه با استفاده از سیستم ماژول، کد
خود را سازماندهی کرده و کپسولهسازی را پیادهسازی کنیم.