سلام، Cargo!

Cargo سیستم ساخت و مدیر بسته‌های Rust است. بیشتر Rustacean ها از این ابزار برای مدیریت پروژه‌های Rust خود استفاده می‌کنند زیرا Cargo بسیاری از وظایف را برای شما انجام می‌دهد، مانند ساختن کد شما، دانلود کتابخانه‌هایی که کد شما به آن‌ها وابسته است، و ساختن آن کتابخانه‌ها. (ما به کتابخانه‌هایی که کد شما به آن‌ها نیاز دارد وابستگی‌ها می‌گوییم.)

ساده‌ترین برنامه‌های Rust، مانند برنامه‌ای که تا کنون نوشته‌ایم، هیچ وابستگی‌ای ندارند. اگر پروژه “Hello, world!” را با Cargo می‌ساختیم، فقط از بخشی از Cargo استفاده می‌کرد که مسئول ساختن کد شما است. هنگامی که برنامه‌های پیچیده‌تری در Rust بنویسید، وابستگی‌ها را اضافه خواهید کرد و اگر پروژه‌ای را با استفاده از Cargo شروع کنید، اضافه کردن وابستگی‌ها بسیار راحت‌تر خواهد بود.

به دلیل اینکه اکثریت عظیم پروژه‌های Rust از Cargo استفاده می‌کنند، بقیه این کتاب فرض می‌کند که شما نیز از Cargo استفاده می‌کنید. Cargo با Rust نصب می‌شود اگر از نصب‌کننده‌های رسمی که در بخش [“نصب”][installation] بحث شده‌اند استفاده کرده باشید. اگر Rust را از طریق روش‌های دیگری نصب کرده‌اید، بررسی کنید که آیا Cargo نصب شده است یا نه با وارد کردن دستور زیر در ترمینال خود:

$ cargo --version

اگر شماره نسخه‌ای مشاهده کردید، آن را دارید! اگر خطای command not found را دیدید، به مستندات روش نصب خود مراجعه کنید تا نحوه نصب جداگانه Cargo را پیدا کنید.

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

بیایید یک پروژه جدید با استفاده از Cargo بسازیم و ببینیم چگونه از پروژه اولیه “Hello, world!” ما متفاوت است. به دایرکتوری projects خود بروید (یا هر جایی که تصمیم گرفته‌اید کد خود را ذخیره کنید). سپس، در هر سیستم‌عاملی، دستور زیر را وارد کنید:

$ cargo new hello_cargo
$ cd hello_cargo

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

به دایرکتوری hello_cargo بروید و فایل‌ها را لیست کنید. خواهید دید که Cargo دو فایل و یک دایرکتوری برای ما ایجاد کرده است: یک فایل Cargo.toml و یک دایرکتوری src که داخل آن یک فایل main.rs است.

همچنین یک مخزن Git جدید به همراه یک فایل .gitignore ایجاد شده است. فایل‌های Git در صورتی که دستور cargo new را در یک مخزن Git موجود اجرا کنید، ایجاد نمی‌شوند؛ می‌توانید این رفتار را با استفاده از cargo new --vcs=git لغو کنید.

نکته: Git یک سیستم کنترل نسخه رایج است. شما می‌توانید دستور cargo new را تغییر دهید تا از سیستم کنترل نسخه‌ای متفاوت یا هیچ سیستم کنترل نسخه‌ای استفاده کند با استفاده از پرچم --vcs. برای دیدن گزینه‌های موجود، دستور cargo new --help را اجرا کنید.

فایل Cargo.toml را در ویرایشگر متن دلخواه خود باز کنید. این فایل باید مشابه کدی باشد که در فهرست 1-2 آمده است.

Filename: Cargo.toml
[package]
name = "hello_cargo"
version = "0.1.0"
edition = "2021"

# برای مشاهده کلیدها و تعاریف بیشتر به https://doc.rust-lang.org/cargo/reference/manifest.html مراجعه کنید

[dependencies]
Listing 1-2: محتویات Cargo.toml که توسط cargo new ایجاد شده است

این فایل در فرمت [TOML][toml] (زبان ساده و آشکار تام) است که فرمت پیکربندی Cargo است.

خط اول، [package]، یک عنوان بخش است که نشان می‌دهد بیانیه‌های بعدی در حال پیکربندی یک بسته هستند. همانطور که اطلاعات بیشتری به این فایل اضافه می‌کنیم، بخش‌های دیگری را اضافه خواهیم کرد.

سه خط بعدی اطلاعات پیکربندی‌ای را تنظیم می‌کنند که Cargo برای کامپایل برنامه شما به آن‌ها نیاز دارد: نام، نسخه و نسخه‌ای از Rust که باید استفاده شود. در مورد کلید edition در [ضمیمه ه][appendix-e] صحبت خواهیم کرد.

آخرین خط، [dependencies]، شروع یک بخش است که شما باید وابستگی‌های پروژه خود را در آن ذکر کنید. در Rust، بسته‌های کد به نام کرِیت‌ها شناخته می‌شوند. برای این پروژه نیازی به کرِیت‌های دیگر نداریم، اما در پروژه اول فصل 2 به آن‌ها نیاز خواهیم داشت، بنابراین در آن زمان از این بخش وابستگی‌ها استفاده خواهیم کرد.

حالا فایل src/main.rs را باز کنید و نگاهی بیندازید:

Filename: src/main.rs

fn main() {
    println!("Hello, world!");
}

Cargo یک برنامه “Hello, world!” برای شما ایجاد کرده است، درست مانند برنامه‌ای که در فهرست 1-1 نوشتیم! تا کنون، تفاوت‌های بین پروژه ما و پروژه‌ای که Cargo ایجاد کرده این است که Cargo کد را در دایرکتوری src قرار داده و ما یک فایل پیکربندی Cargo.toml در دایرکتوری بالای پروژه داریم.

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

اگر پروژه‌ای شروع کرده‌اید که از Cargo استفاده نمی‌کند، همانطور که در پروژه “Hello, world!” انجام دادیم، می‌توانید آن را به پروژه‌ای که از Cargo استفاده می‌کند تبدیل کنید. کد پروژه را به دایرکتوری src منتقل کرده و یک فایل Cargo.toml مناسب ایجاد کنید. یکی از راه‌های آسان برای به‌دست آوردن آن فایل Cargo.toml این است که دستور cargo init را اجرا کنید که به‌طور خودکار آن را برای شما ایجاد می‌کند.

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

حالا بیایید ببینیم که چه تفاوتی در زمانی که برنامه “Hello, world!” را با Cargo می‌سازیم و اجرا می‌کنیم وجود دارد! از دایرکتوری hello_cargo خود، پروژه را با وارد کردن دستور زیر بسازید:

$ 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 در ویندوز) ایجاد می‌کند به جای این که آن را در دایرکتوری فعلی شما قرار دهد. زیرا ساخت پیش‌فرض یک ساخت دیباگ است، Cargo فایل باینری را در دایرکتوری به نام debug قرار می‌دهد. شما می‌توانید فایل اجرایی را با این دستور اجرا کنید:

$ ./target/debug/hello_cargo # یا .\target\debug\hello_cargo.exe در ویندوز
Hello, world!

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

ما همین حالا پروژه را با دستور cargo build ساختیم و با ./target/debug/hello_cargo اجرا کردیم، اما همچنین می‌توانیم از cargo run برای کامپایل کردن کد و سپس اجرای باینری حاصل در یک دستور استفاده کنیم:

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

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

توجه کنید که این بار خروجی‌ای که نشان دهد Cargo در حال کامپایل کردن hello_cargo است، مشاهده نکردیم. Cargo متوجه شد که فایل‌ها تغییر نکرده‌اند، بنابراین بازسازی نکرد و فقط باینری را اجرا کرد. اگر کد منبع خود را تغییر داده بودید، 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 همچنین یک دستور به نام cargo check را فراهم می‌کند. این دستور کد شما را به سرعت بررسی می‌کند تا مطمئن شود که کامپایل می‌شود اما هیچ اجرایی تولید نمی‌کند:

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

چرا شما به یک فایل اجرایی نیاز ندارید؟ اغلب، cargo check بسیار سریع‌تر از cargo build است زیرا مرحله تولید یک فایل اجرایی را رد می‌کند. اگر شما به طور مداوم در حال بررسی کد خود هستید، استفاده از cargo check سرعت فرایند اطلاع دادن به شما از این که پروژه هنوز کامپایل می‌شود را افزایش می‌دهد! به همین دلیل، بسیاری از Rustaceans به طور دوره‌ای cargo check را در حین نوشتن کد خود اجرا می‌کنند تا مطمئن شوند که پروژه‌شان کامپایل می‌شود. سپس زمانی که آماده استفاده از باینری شدند، از دستور cargo build استفاده می‌کنند.

بیایید خلاصه‌ای از آنچه که تا به حال در مورد Cargo آموخته‌ایم مرور کنیم:

  • ما می‌توانیم یک پروژه با استفاده از cargo new بسازیم.
  • ما می‌توانیم یک پروژه را با استفاده از cargo build بسازیم.
  • ما می‌توانیم یک پروژه را با یک مرحله از ساخت و اجرا با استفاده از cargo run بسازیم و اجرا کنیم.
  • ما می‌توانیم یک پروژه را بدون تولید باینری برای بررسی خطاها با استفاده از cargo check بسازیم.
  • به جای ذخیره نتیجه ساخت در همان دایرکتوری که کد ما قرار دارد، Cargo آن را در دایرکتوری target/debug ذخیره می‌کند.

یک مزیت اضافی استفاده از Cargo این است که دستورات آن در همه سیستم‌عامل‌ها یکسان است. بنابراین، از این پس، دیگر دستورالعمل‌های خاصی برای لینوکس و macOS در مقابل ویندوز ارائه نخواهیم کرد.

ساخت برای انتشار

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

Cargo به عنوان یک کنوانسیون

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

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

$ git clone example.org/someproject
$ cd someproject
$ cargo build

برای اطلاعات بیشتر در مورد Cargo، می‌توانید به مستندات آن مراجعه کنید.

خلاصه

شما در حال حاضر شروع بسیار خوبی برای سفر خود در Rust دارید! در این فصل، شما یاد گرفته‌اید که چگونه:

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

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