مقدمه

اکوسیستم Rust مجهز به یک ابزار بسیار کارامد و عالی برای مدیریت وابستگی‌ها (dependencies) و ساخت (build) پروژه‌هاست. این ابزار Cargo نام دارد و انجام کارهایی مانند ساخت و اجرای برنامه‌ها، دانلود پکیج‌های خارجی مورد نیاز برنامه و مدیریت کتابخانه‌ها و وابستگی‌های مورد نیاز برنامه را به سادگی انجام می‌دهد. در واقع، Cargo در دنیای Rust یک نقش دوگانه دارد: یکی به عنوان build tool و دیگری به عنوان dependency manger. برنامه‌های ساده و کوچکی مانند برنامه‌ی Hello, world! به هیچ کتابخانه یا ماژول خارجی نیاز ندارند و به عبارت دیگر، فاقد هرگونه وابستگی هستند و لذا چنانچه از Cargo در تولید چنین برنامه‌هایی استفاده کنیم، تنها از جنبه‌ای از این ابزار بهره برده‌ایم که به فرایند ساخت (build) پروژه مربوط است. اما پروژه‌های بزرگتر به پکیج‌ها و کتابخانه‌های متعددی نیاز دارند و در مورد این پروژه‌ها بدون استفاده از Cargo کار افزودن کتابخانه‌ها و مدیریت آنها طاقت‌فرسا خواهد بود. ابزار Cargo به همراه کامپایلر rustc نصب می‌شود و نیازی به نصب آن به صورت دستی نیست. می‌توانید با استفاده از دستور cargo --version از نصب این ابزار روی سیستم خود مطمئن شوید.

ایجاد پروژه با Cargo

اجازه دهید یک پروژه‌ی جدید را با استفاده از Cargo ایجاد کنیم و تفاوت‌های آن را نسبت به روشی که برای ساخت پروژه‌ی Hello, world! به کار گرفتیم، ببینیم.

به دایرکتوری projects که در درس قبل ایجاد کردیم (و یا هر مکان دیگری که قصد دارید پروژه‌ی خود را ذخیره کنید)‌ بروید و دستورات زیر را اجرا کنید.

$ cargo new hello_cargo
$ cd hello_cargo

اجرای دستور اول باعث ایجاد یک دایرکتوری جدید با نام hello_cargo و فایل‌هایی در درون این دایرکتوری می‌شود. در واقع، hello_cargo نامی است که ما برای پروژه‌ی خود تعیین کرده‌ایم و با اجرای این دستور، پوشه‌ای با همین نام به عنوان دایرکتوری این پروژه ایجاد می‌شود و فایل‌های مورد نیاز پروژه نیز به طور خودکار توسط Cargo ایجاد شده و درون این دایرکتوری قرار می‌گیرند. این فایل‌ها عبارتند از: یک فایل پیکربندی با نام Cargo.toml و یک فایل منبع با نام main.rs که در پوشه‌ای با نام src قرار دارد. البته فایل .gitignore نیز در این دایرکتوری دیده می‌شود که به سیستم کنترل نسخه‌ی Git مربوط است.

Git یک سیستم کنترل نسخه (Version Control System) یا VCS بسیار محبوب است که انتخاب پیش‌فرض Cargo برای فرایند کنترل نسخه در یک پروژه است. اما شما می‌توانید با استفاده از آپشن --vcs دستور cargo new را طوری تغییر دهید که از یک VCS دیگر استفاده کند و یا حتی پروژه را بدون VCS ایجاد کند. دستور cargo new --help را برای مشاهده‌ی گزینه‌های ممکن اجرا کنید.

فایل Cargo.toml را با یک ویرایشگر یا IDE باز کنید. محتویات این فایل شبیه زیر است:

Cargo.toml
[package]
name = "hello_cargo"
version = "0.1.0"
authors = ["Your Name <you@example.com>"]
edition = "2021"
            
[dependencies]

این فایل در فرمت TOML یا Toms Obvious, Minimal Language است که فرمت پیکربندی (configuration) مربوط به Cargo است. این فایل از دو بخش تشکیل شده: بخش package که شامل اطلاعات مربوط به پیکربندی پروژه است و بخش dependencies که شامل لیست پکیج‌های مورد نیاز پروژه یا همان وابستگی‌های پروژه است.

در بخش package اطلاعاتی مانند نام، ورژن و نویسنده‌ی برنامه و نسخه‌ای از Rust که برای نوشتن برنامه به کار گرفته شده، دیده می‌شود. Cargo اطلاعات مربوط به نام و ایمیل شما را از محیط شما استخراج می‌کند و در صورت نادرست بودن این اطلاعات، می‌توانید آنها را اصلاح کرده و فایل را مجدداً‌ ذخیره کنید. در بخش dependencies نیز فعلاً هیچ اطلاعاتی وجود ندارد، چون پروژه‌ی ما به هیچ کتابخانه‌ی خارجی نیاز ندارد. در Rust کتابخانه‌های خارجی را crate می‌نامند. بر خلاف این پروژه، در فصل بعدی با پروژه‌ای سر و کار خواهیم داشت که به یک یا چند کتابخانه خارجی نیاز دارد و در آنجا خواهید دید که اطلاعات مربوط به آن کتابخانه‌ها چگونه در بخش dependencies قرار می‌گیرد.

حالا فایل main.rs را که در دایرکتوری src قرار دارد، باز کنید و محتویات آن را ببینید.

Copy Icon src/main.rs
fn main() {
  println!("Hello, world!");
}          

همانطور که می‌بینید، Cargo یک برنامه‌ی Hello, world! مشابه آنچه قبلاً خودمان نوشته بودیم، برای ما ایجاد کرده است. اما تفاوت‌هایی بین پروژه‌ای که ما ایجاد کردیم با پروژه‌ی ایجاد شده توسط Cargo وجود دارذ: Cargo فایل main.rs را درون یک دایرکتوری با نام src قرار داده است و یک فایل پیکربندی با نام Cargo.toml نیز درون ریشه‌ی (root) دایرکتوری پروژه قرار داده است.

روال کار Cargo این است که فایل‌های منبع و کد را درون دایرکتوری src قرار می‌دهد و در ریشه‌ی دایرکتوری پروژه باید فقط فایل‌های پیکربندی، اطلاعات مربوط به لایسنس و به طور کلی هر چیز غیرمرتبط با کد قرار داشته باشد. پیروی از این روال، کار سازماندهی و مدیریت پروژه را ساده‌تر می‌کند.

اگر بخواهیم پروژه‌ای را که بدون استفاده از Cargo ایجاد شده به یک پروژه‌ی تحت مدیریت Cargo تبدیل کنیم، کافیست کد پروژه را به دایرکتوری src منتقل کنیم و یک فایل Cargo.toml مناسب ایجاد کنیم و در ریشه‌ی دایرکتوری پروژه قرار دهیم.

ساخت و اجرای یک پروژه با Cargo

حالا اجازه دهید ببینیم برنامه‌های تحت مدیریت Cargo را چطور build و اجرا می‌کنیم. از درون دایرکتوری hello_cargo پروژه‌ی خود را با وارد کردن دستور زیر build کنید:

$ cargo build
  Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)
   Finished dev [unoptimized + debuginfo] target(s) in 2.85 secs

این دستور باعث ایجاد یک فایل اجرایی در مسیر target/debug/hello_cargo یا target\debug\hello_cargo.exe در ویندوز شده که با استفاده از دستور زیر می‌توانیم این فایل را اجرا کنیم:

$ ./target/debug/hello_cargo # or .\target\debug\hello_cargo.exe on Windows
Hello, world!

اگر همه چیز درست باشد، باید عبارت Hello, world! را در خروجی ببینید. وقتی دستور cargo build را برای اولین بار اجرا می‌کنیم، فایل دیگری با نام Cargo.lock در دایرکتوری پروژه توسط Cargo ایجاد می‌شود. این فایل ورژن‌های دقیق وابستگی‌ها را در پروژه ردیابی می‌کند. توجه داشته باشید که فایل Cargo.lock هیچ‌وقت نیازی به ویرایش دستی ما ندارد و Cargo مدیریت محتوای آن را بر عهده دارد.

به این ترتیب، ما با استفاده از دستور cargo build پروژه را ساختیم و سپس فایل اجرایی تولید شده را مستقیماً‌ اجرا کردیم، اما به جای این کار، می‌توانیم از دستور cargo run استفاده کنیم. استفاده از این دستور باعث می‌شود که هر دو مرحله‌ی کامپایل و اجرای فایل تولید شده با استفاده از همین تک‌دستور انجام شود.

$ cargo run
  Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
   Running `target/debug/hello_cargo`
Hello, world!

اگر به خروجی بالا دقت کنید، متوجه می‌شوید که این بار کامپایل انجام نشده است. زیرا Cargo متوجه شده که از کامپایل قبلی هیچ تغییری روی فایل ایجاد نشده و بنابراین، فقط اقدام به اجرای مجدد فایل اجرایی کرده است. اما اگر تغییری در کدها ایجاد کرده و مجدداً دستور cargo run را اجرا کنیم، Cargo کامپایل مجدد را انجام داده و سپس اقدام به اجرای فایل می‌کند و لذا خروجی به صورت زیر خواهد بود:

$ cargo run
  Compiling hello_cargo v0.1.0 (file:///projects/hello_cargo)
   Finished dev [unoptimized + debuginfo] target(s) in 0.33 secs
    Running `target/debug/hello_cargo`
Hello, world!

علاوه بر این دستورات، دستور cargo check نیز فراهم شده که برای کامپایل بدون تولید فایل اجرایی کاربرد دارد. در واقع، با اجرای این دستور Cargo کد ما را بررسی کرده و در صورت لزوم کامپایل می‌کند اما فایل اجرایی را تولید نخواهد کرد.

$ cargo check
  Checking hello_cargo v0.1.0 (file:///projects/hello_cargo)
   Finished dev [unoptimized + debuginfo] target(s) in 0.32 secs

مزیتی که این دستور نسبت به دستور cargo build دارد، سریع‌تر بودن آن است. اگر هنگام کدنویسی مرتباً کار خود را چک می‌کنید، این دستور این فرایند را سریع‌تر انجام می‌دهد.

وقتی پروژه برای انتشار نهایی آماده شد، بهتر است که از دستور cargo build --release استفاده کنیم. این دستور کامپایل پروژه را همراه با بهینه‌سازی‌هایی در راستای افزایش سرعت اجرای برنامه انجام می‌دهد. استفاده از این دستور، فایل اجرایی تولید شده را نه در target/debug بلکه در target/release قرار می‌دهد. توجه داشته باشید که اگرچه بهینه‌سازی‌های ناشی از اجرای این دستور باعث می‌شوند که سرعت اجرای برنامه افزایش یابد اما در عوض، زمان کامپایل برنامه را طولانی‌تر می‌کنند. خلاصه اینکه از هر یک از دستوراتی که برای ساخت و اجرای پروژه معرفی کردیم، باید در جای مناسب خود استفاده شود.

اجازه دهید مطالب گفته شده در مورد Cargo را خلاصه کنیم:

  • از دستور cargo build برای کامپایل پروژه استفاده می‌کنیم.
  • با استفاده از دستور cargo run می‌توانیم ساخت و اجرای پروژه را در یک گام انجام دهیم.
  • دستور cargo check می‌تواند باعث شود که یک پروژه ساخته یا build شود اما فایل اجرایی برنامه تولید نشود.
  • استفاده از دستور cargo build --release باعث می‌شود که کامپایل پروژه با بهینه‌سازی‌هایی در راستای افزایش سرعت اجرای برنامه انجام شود.
  • Cargo نتیجه‌ی build را نه در دایرکتوری کدها بلکه در دایرکتوری target/debug ذخیره می‌کند.

یک مزیت دیگر استفاده از Cargo یکسان بودن دستورات مورد نیاز برای ساخت و اجرای پروژه در همه‌ی سیستم‌عامل‌هاست. به این ترتیب، دیگر نیازی به بیان دستورات مجزا برای لینوکس و مک از یک‌سو و ویندوز از سوی دیگر نداریم.

خلاصه

با به پایان رساندن این فصل، یک شروع خوب در سفر به دنیای Rust رقم زده‌اید. در این فصل یاد گرفتید که:

  • آخرین نسخه‌ی پایدار از کامپایلر rustc را با استفاده از ابزار rustup نصب کنید.
  • بروزرسانی به یک نسخه‌ی جدید Rust را انجام دهید.
  • مستندات Rust را به طور آفلاین مشاهده کنید.
  • با استفاده‌ی مستقیم از rustc یک برنامه‌ی Hello, world! را ایجاد و اجرا کنید.
  • یک پروژه را با استفاده از Cargo ایجاد و اجرا کنید.

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