مقدمه

برنامه ما در حال حاضر تمام خروجی‌ها، چه نتایج موفق جستجو و چه پیام‌های خطا، را به ترمینال و با استفاده از ماکروی println! چاپ می‌کند. این برای استفاده تعاملی کاربر مشکلی ایجاد نمی‌کند، اما ابزارهای خط فرمان حرفه‌ای باید به گونه‌ای طراحی شوند که قابل ترکیب با ابزارهای دیگر باشند.

در محیط‌های شبه-یونیکس، یک قرارداد رایج این است که خروجی موفق یک برنامه به «جریان خروجی استاندارد» یا standard output (stdout) و تمام پیام‌های خطا به «جریان خطای استاندارد» یا standard error (stderr) ارسال شوند. این تفکیک به کاربران اجازه می‌دهد تا خروجی موفق یک برنامه را به یک فایل هدایت کنند (> output.txt) و همچنان پیام‌های خطا را در ترمینال مشاهده کنند.

چاپ خطا در stderr

در حال حاضر، ما پیام‌های خطا را با println! در تابع main چاپ می‌کنیم. برای ارسال این پیام‌ها به stderr، باید از ماکروی eprintln! استفاده کنیم. این ماکرو دقیقاً مانند println! کار می‌کند، اما خروجی خود را به جای stdout، به stderr می‌نویسد.

Copy Icon src/main.rs
fn main() {
    let args: Vec<String> = env::args().collect();

    let config = Config::build(&args).unwrap_or_else(|err| {
        // Print error messages to standard error instead of standard output
        eprintln!("Problem parsing arguments: {err}");
        process::exit(1);
    });

    // -- snip --

    if let Err(e) = minigrep::run(config) {
        eprintln!("Application error: {e}");
        process::exit(1);
    }
}

ما به سادگی تمام فراخوانی‌های println! در بلوک‌های مدیریت خطا را با eprintln! جایگزین کرده‌ایم.

تست عملکرد جدید

حالا بیایید رفتار جدید برنامه را تست کنیم. ابتدا یک اجرای موفق را امتحان کرده و خروجی آن را به یک فایل هدایت می‌کنیم:

$ cargo run -- to poem.txt > output.txt
                    

این دستور هیچ خروجی‌ای در ترمینال نمایش نخواهد داد. اگر فایل output.txt را باز کنید، خواهید دید که نتایج جستجو در آن ذخیره شده است. این نشان می‌دهد که خروجی موفق برنامه به درستی به stdout ارسال شده است.

حالا یک حالت خطا را امتحان می‌کنیم، مثلاً با پاس ندادن آرگومان‌های کافی:

$ cargo run > output.txt
Problem parsing arguments: not enough arguments
                    

این بار، با وجود اینکه ما خروجی استاندارد را به یک فایل هدایت کرده‌ایم، پیام خطا همچنان در ترمینال نمایش داده می‌شود. این رفتار صحیح و مورد انتظار برای یک ابزار خط فرمان است و نشان می‌دهد که پیام خطای ما به درستی به stderr ارسال شده است.

در این درس با تفکیک بین جریان‌های خروجی stdout و stderr و نحوه استفاده از ماکروی eprintln! برای نوشتن برنامه‌های خط فرمان حرفه‌ای‌تر آشنا شدیم. با این درس، فصل «ساخت یک برنامه CLI» به پایان می‌رسد. ما در این فصل، با یک پروژه عملی، مفاهیم مهمی مانند کار با آرگومان‌ها، خواندن فایل‌ها، بازسازی کد، توسعه تست‌محور و مدیریت صحیح خطا را به کار بستیم. در فصل بعدی، به سراغ «ویژگی‌های Functional در Rust» خواهیم رفت و با مفاهیمی مانند Closureها و Iteratorها آشنا می‌شویم که به ما اجازه می‌دهند کدهای گویاتر و کارآمدتری بنویسیم.