Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Viết Thông Báo Lỗi vào Standard Error Thay vì Standard Output

Hiện tại, chúng ta đang viết tất cả đầu ra của mình vào terminal bằng cách sử dụng macro println!. Trong hầu hết các terminal, có hai loại đầu ra: standard output (stdout) cho thông tin chung và standard error (stderr) cho thông báo lỗi. Sự phân biệt này cho phép người dùng lựa chọn chuyển hướng đầu ra thành công của chương trình vào một file nhưng vẫn in thông báo lỗi ra màn hình.

Macro println! chỉ có khả năng in vào standard output, vì vậy chúng ta phải sử dụng một cách khác để in vào standard error.

Kiểm tra Nơi Lỗi Được Ghi

Đầu tiên, hãy quan sát cách nội dung được in bởi minigrep hiện đang được ghi vào standard output, bao gồm cả các thông báo lỗi mà chúng ta muốn viết vào standard error thay thế. Chúng ta sẽ làm điều đó bằng cách chuyển hướng luồng standard output vào một file trong khi chủ ý gây ra lỗi. Chúng ta sẽ không chuyển hướng luồng standard error, vì vậy bất kỳ nội dung nào được gửi đến standard error sẽ tiếp tục hiển thị trên màn hình.

Các chương trình dòng lệnh được mong đợi sẽ gửi thông báo lỗi đến standard error stream để chúng ta vẫn có thể thấy thông báo lỗi trên màn hình ngay cả khi chúng ta chuyển hướng standard output stream vào một tệp. Chương trình của chúng ta hiện không hoạt động tốt: chúng ta sắp thấy rằng nó lưu đầu ra thông báo lỗi vào một file thay vì màn hình!

Để chứng minh hành vi này, chúng ta sẽ chạy chương trình với > và đường dẫn file, output.txt, nơi chúng ta muốn chuyển hướng luồng standard output đến. Chúng ta sẽ không truyền bất kỳ đối số nào, điều đó sẽ gây ra lỗi:

$ cargo run > output.txt

Cú pháp > nói với shell rằng hãy ghi nội dung của standard output vào output.txt thay vì màn hình. Chúng ta không thấy thông báo lỗi mà chúng ta đã mong đợi in trên màn hình, vì vậy điều đó có nghĩa là nó hẳn đã xuất hiện trong file. Đây là nội dung của output.txt:

Problem parsing arguments: not enough arguments

Đúng vậy, thông báo lỗi của chúng ta đang được in vào standard output. Sẽ hữu ích hơn nhiều nếu các thông báo lỗi như thế này được in ra standard error để chỉ dữ liệu từ một lần chạy thành công mới xuất hiện trong file. Chúng ta sẽ thay đổi điều đó.

In Lỗi vào Standard Error

Chúng ta sẽ sử dụng mã trong Listing 12-24 để thay đổi cách thông báo lỗi được in. Vì việc cấu trúc lại mã mà chúng ta đã làm trước đó trong chương này, tất cả mã in thông báo lỗi nằm trong một hàm, main. Thư viện chuẩn cung cấp macro eprintln! để in vào standard error stream, vì vậy hãy thay đổi hai nơi chúng ta đã gọi println! để in lỗi bằng cách sử dụng eprintln! thay thế.

use std::env;
use std::error::Error;
use std::fs;
use std::process;

use minigrep::{search, search_case_insensitive};

fn main() {
    let args: Vec<String> = env::args().collect();

    let config = Config::build(&args).unwrap_or_else(|err| {
        eprintln!("Problem parsing arguments: {err}");
        process::exit(1);
    });

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

pub struct Config {
    pub query: String,
    pub file_path: String,
    pub ignore_case: bool,
}

impl Config {
    fn build(args: &[String]) -> Result<Config, &'static str> {
        if args.len() < 3 {
            return Err("not enough arguments");
        }

        let query = args[1].clone();
        let file_path = args[2].clone();

        let ignore_case = env::var("IGNORE_CASE").is_ok();

        Ok(Config {
            query,
            file_path,
            ignore_case,
        })
    }
}

fn run(config: Config) -> Result<(), Box<dyn Error>> {
    let contents = fs::read_to_string(config.file_path)?;

    let results = if config.ignore_case {
        search_case_insensitive(&config.query, &contents)
    } else {
        search(&config.query, &contents)
    };

    for line in results {
        println!("{line}");
    }

    Ok(())
}

Bây giờ hãy chạy chương trình một lần nữa theo cùng một cách, không có đối số nào và chuyển hướng standard output với >:

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

Bây giờ chúng ta thấy lỗi trên màn hình và output.txt không chứa gì cả, đó là hành vi mà chúng ta mong đợi từ các chương trình dòng lệnh.

Hãy chạy chương trình một lần nữa với các đối số không gây ra lỗi nhưng vẫn chuyển hướng standard output vào một file, như sau:

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

Chúng ta sẽ không thấy bất kỳ đầu ra nào trên terminal, và output.txt sẽ chứa các kết quả của chúng ta:

Filename: output.txt

Are you nobody, too?
How dreary to be somebody!

Điều này chứng minh rằng chúng ta hiện đang sử dụng standard output cho đầu ra thành công và standard error cho đầu ra lỗi một cách thích hợp.

Tóm tắt

Chương này đã nhắc lại một số khái niệm chính mà bạn đã học cho đến nay và đã đề cập đến cách thực hiện các hoạt động I/O thông thường trong Rust. Bằng cách sử dụng đối số dòng lệnh, files, biến môi trường, và macro eprintln! để in lỗi, bạn đã sẵn sàng để viết các ứng dụng dòng lệnh. Kết hợp với các khái niệm trong các chương trước, mã của bạn sẽ được tổ chức tốt, lưu trữ dữ liệu hiệu quả trong các cấu trúc dữ liệu phù hợp, xử lý lỗi một cách tốt và được kiểm tra kỹ lưỡng.

Tiếp theo, chúng ta sẽ khám phá một số tính năng của Rust bị ảnh hưởng bởi các ngôn ngữ lập trình hàm: closures và iterators.