輸出
輸出「Hello World」
#![allow(unused)] fn main() { println!("Hello World"); }
好,這很簡單。太棒了,進入下一個主題。
使用 println!
您可以使用 println!
巨集輸出您喜歡的所有內容。這個巨集有一些非常強大的功能,但也有特殊的語法。它預期您將字串字面值寫成第一個參數,這個字串字面值包含佔位符,這些佔位符將由緊接著後續參數的值填入。
例如,
#![allow(unused)] fn main() { let x = 42; println!("My lucky number is {}.", x); }
會輸出
My lucky number is 42.
上述字串中的大括號({}
)是這些佔位符之一。這是預設的佔位符類型,會嘗試以人類可讀的方式輸出給定的值。對於數字和字串,這是非常好用的方式,但不是所有類型都能做到。因此,這裡也有「除錯表示法」,您可以透過以下方式填入佔位符的大括號來取得:「{:?}
」
例如,
#![allow(unused)] fn main() { let xs = vec![1, 2, 3]; println!("The list is: {:?}", xs); }
會輸出
The list is: [1, 2, 3]
如果您希望自己的資料類型可用於除錯和記錄,在大部分情況下,可以在定義上方加入 #[derive(Debug)]
。
輸出錯誤訊息
輸出錯誤訊息應該透過 stderr
進行,讓使用者和其它工具更容易將它們的輸出導向至檔案或更多工具。
在 Rust 中,這是透過 println!
和 eprintln!
達成,前者會印到 stdout
,後者則印到 stderr
。
#![allow(unused)] fn main() { println!("This is information"); eprintln!("This is an error! :("); }
關於印出效能的備註
印到終端機的效能令人驚訝的低落!如果你在迴圈內呼叫類似 println!
的東西,在原本快速的程式中可能會輕易造成瓶頸。可有兩件事能加速這件事。
首先,你可能想要減少實際「快取」到終端機的寫入次數。println!
會告知系統每次快取到終端機,因為通常會印出每一行。如果你不需要這樣,你可以用 BufWriter
包覆你的 stdout
句柄,預設緩衝大小至 8 kB。(你仍然可以在想要立即印出的時候呼叫 BufWriter
的 .flush()
。)
#![allow(unused)] fn main() { use std::io::{self, Write}; let stdout = io::stdout(); // get the global stdout entity let mut handle = io::BufWriter::new(stdout); // optional: wrap that handle in a buffer writeln!(handle, "foo: {}", 42); // add `?` if you care about errors here }
其次,鎖定 stdout
(或 stderr
)並使用 writeln!
直接印到它裡頭會很有幫助。這將可避免系統反覆鎖定和解鎖 stdout
。
#![allow(unused)] fn main() { use std::io::{self, Write}; let stdout = io::stdout(); // get the global stdout entity let mut handle = stdout.lock(); // acquire a lock on it writeln!(handle, "foo: {}", 42); // add `?` if you care about errors here }
你也可以同時採用這兩種方式。
顯示進度條
有些 CLI 應用程式執行的時間不到一秒,有些則需要幾分鐘或幾小時。如果你正在撰寫後者的程式,你或許會想要讓使用者知道正在執行某些事。為此,你應該嘗試印出有用的狀態更新,理想上以容易閱讀的形式。
使用 indicatif 板條箱,你可以為你的程式增加進度條和小型的旋轉圖示。以下是一個快速範例
fn main() {
let pb = indicatif::ProgressBar::new(100);
for i in 0..100 {
do_hard_work();
pb.println(format!("[+] finished #{}", i));
pb.inc(1);
}
pb.finish_with_message("done");
}
記錄
為了讓你在執行程式時更容易了解正在發生什麼事,我們可能會想要加入一些記錄陳述。這通常在你撰寫應用程式時就能夠輕鬆做到。但當你在半年後再次執行這個程式時就會發現這樣做非常有幫助。記錄在某種程度上與使用 println!
相同,但你可以指定訊息的重要性。你通常可以使用以下層級:error、warn、info、debug 和 trace(error 的優先順序最高,trace 最低)。
要將簡單記錄加入你的應用程式,你需要兩個東西:log crate(它包含命名自記錄層級的巨集)和實際將記錄輸出寫入某個實用位置的介接器。能夠使用記錄介接器非常靈活:例如,你可以使用它們將記錄寫入終端機,也可以寫入 syslog,或是寫入集中式記錄伺服器。
由於我們現在只關注撰寫 CLI 應用程式,因此一個好用的介接器是 env_logger。它稱為「env」記錄器,是因為你可以使用環境變數來指定應用程式的哪些部分你要記錄(以及你要記錄的層級)。它會在記錄訊息中加上時間戳記和記錄訊息來自的模組。由於函式庫也可以使用 log
,因此你也可以輕易設定他們的記錄輸出。
以下是快速範例
use log::{info, warn};
fn main() {
env_logger::init();
info!("starting up");
warn!("oops, nothing implemented!");
}
假設你的檔案為 src/bin/output-log.rs
,在 Linux 和 macOS 上,你可以這樣執行
$ env RUST_LOG=info cargo run --bin output-log
Finished dev [unoptimized + debuginfo] target(s) in 0.17s
Running `target/debug/output-log`
[2018-11-30T20:25:52Z INFO output_log] starting up
[2018-11-30T20:25:52Z WARN output_log] oops, nothing implemented!
在 Windows PowerShell 中,你可以這樣執行
$ $env:RUST_LOG="info"
$ cargo run --bin output-log
Finished dev [unoptimized + debuginfo] target(s) in 0.17s
Running `target/debug/output-log.exe`
[2018-11-30T20:25:52Z INFO output_log] starting up
[2018-11-30T20:25:52Z WARN output_log] oops, nothing implemented!
在 Windows CMD 中,你可以這樣執行
$ set RUST_LOG=info
$ cargo run --bin output-log
Finished dev [unoptimized + debuginfo] target(s) in 0.17s
Running `target/debug/output-log.exe`
[2018-11-30T20:25:52Z INFO output_log] starting up
[2018-11-30T20:25:52Z WARN output_log] oops, nothing implemented!
RUST_LOG
是你可以用來設定記錄設定的環境變數名稱。env_logger
還包含建構器,因此你可以以編程方式調整這些設定,例如預設顯示 info 等級訊息。
有很多替代的記錄介接器,以及 log
的替代方案或擴充功能。如果你知道你的應用程式有許多記錄需要處理,請務必檢閱它們,並讓使用者的生活更輕鬆。