更多關於列印

在 Rust 中,你幾乎可以用任何你想要的方式列印東西。這裡可以知道更多關於列印的事情。

加入 \n 將會產生一個新行(newline),而 \t 將會產生定位字元(tab):

fn main() {
    // Note: 這是 print!, 不是 println!
    print!("\t Start with a tab\nand move to a new line");
}

印出:

         Start with a tab
and move to a new line

"" 裡面可以寫上許多行都沒有問題,但是要注意間距:

fn main() {
    // Note: 第一行後你要從最左邊開始。
    // 如果你直接寫在 println! 下面,它會加入開頭的空白
    println!("Inside quotes
you can write over
many lines
and it will print just fine.");

    println!("If you forget to write
    on the left side, the spaces
    will be added when you print.");
}

印出:

Inside quotes
you can write over
many lines
and it will print just fine.
If you forget to write
    on the left side, the spaces
    will be added when you print.

如果你想印出 \n 這樣的字元(稱為"跳脫字元"),你可以多加一個額外的 \

fn main() {
    println!("Here are two escape characters: \\n and \\t");
}

印出:

Here are two escape characters: \n and \t

有時你有太多的 " 和跳脫字元,並希望 Rust 忽略所有要處理的東西。要做到這件事,你可以在開頭加上 r#,在結尾加上 #

fn main() {
    println!("He said, \"You can find the file at c:\\files\\my_documents\\file.txt.\" Then I found the file."); // 這裡用了 \ 五次
    println!(r#"He said, "You can find the file at c:\files\my_documents\file.txt." Then I found the file."#)
}

這會印出一樣的東西,但是用 r# 使人更容易閱讀。

He said, "You can find the file at c:\files\my_documents\file.txt." Then I found the file.
He said, "You can find the file at c:\files\my_documents\file.txt." Then I found the file.

如果你需要在內容裡面印出 #,那麼你可以用 r## 開頭,用 ## 結尾。如果你要印超過一個 #,兩邊要再各多加一個 #。

這有四個範例:

fn main() {

    let my_string = "'Ice to see you,' he said."; // 單引號
    let quote_string = r#""Ice to see you," he said."#; // 雙引號
    let hashtag_string = r##"The hashtag #IceToSeeYou had become very popular."##; // 一個 # 所以我們至少要用 ##
    let many_hashtags = r####""You don't have to type ### to use a hashtag. You can just use #.""####; // 有三個 ### 所以我們至少要用 ####

    println!("{}\n{}\n{}\n{}\n", my_string, quote_string, hashtag_string, many_hashtags);

}

會印出:

'Ice to see you,' he said.
"Ice to see you," he said.
The hashtag #IceToSeeYou had become very popular.
"You don't have to type ### to use a hashtag. You can just use #."

r# 還有另一個用途:你能用它來把關鍵字(如 letfn 等)當作變數名稱。

fn main() {
    let r#let = 6; // 變數名是 let
    let mut r#mut = 10; // 變數名是 mut
}

r# 之所以有這個功能,是因為舊版的 Rust 關鍵字比現在的少。所以有了 r# 以前不是關鍵字的變數名就能避免出錯。

又或者因為某些原因,你 確實 需要一個名字像是 return 的函式。那麼你可以這樣寫:

fn r#return() -> u8 {
    println!("Here is your number.");
    8
}

fn main() {
    let my_number = r#return();
    println!("{}", my_number);
}

印出:

Here is your number.
8

所以你大概不會需要它,但是如果你真的需要用關鍵字當變數,那就用 r#

如果你想印出 &strchar 的位元組,你可以在字串前寫上 b 就可以了。這適用於所有 ASCII 字元。以下這些是所有的 ASCII 字元:

☺☻♥♦♣♠♫☼►◄↕‼¶§▬↨↑↓→∟↔▲▼123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~

所以,當你印出這個程式:

fn main() {
    println!("{:?}", b"This will look like numbers");
}

結果是這樣:

[84, 104, 105, 115, 32, 119, 105, 108, 108, 32, 108, 111, 111, 107, 32, 108, 105, 107, 101, 32, 110, 117, 109, 98, 101, 114, 115]

char 來說,這叫做 位元組,對 &str 來說,這叫做 位元組字串

如果有需要,你也可以把 br 放在一起:

fn main() {
    println!("{:?}", br##"I like to write "#"."##);
}

它會印出 [73, 32, 108, 105, 107, 101, 32, 116, 111, 32, 119, 114, 105, 116, 101, 32, 34, 35, 34, 46]

還有一個 Unicode 轉義(escape),可以讓你在字串中印出任何 Unicode 字元:\u{}{} 裡面要有一個可以列印的十六進位制數字。這個是說明如何獲得 Unicode 數字及如何再把它印出來的簡短例子。

fn main() {
    println!("{:X}", '행' as u32); // char 轉型 u32 來取得十六進位值
    println!("{:X}", 'H' as u32);
    println!("{:X}", '居' as u32);
    println!("{:X}", 'い' as u32);

    println!("\u{D589}, \u{48}, \u{5C45}, \u{3044}"); // 試著以 unicode 轉義 \u 印出它們
}

我們知道 println! 可以用 {}(用於顯示) 或 {:?}(用於除錯) 來列印,再加上 {:#?} 可以進行漂亮列印。但是還有許多其他列印方式。

例如,如果你有一個變數參考,你可以用 {:p} 來印出 指標地址。指標地址指的是電腦記憶體中的位置。

fn main() {
    let number = 9;
    let number_ref = &number;
    println!("{:p}", number_ref);
}

這會印出 0xe2bc0ffcfc 或者其它地址。它可能每次都不一樣,這取決於你的電腦在哪裡儲存它。

或者你可以列印二進位、十六進位和八進位的值:

fn main() {
    let number = 555;
    println!("Binary: {:b}, hexadecimal: {:x}, octal: {:o}", number, number, number);
}

印出了 Binary: 1000101011, hexadecimal: 22b, octal: 1053

或者你可以加上數字來改變順序。第一個變數將在索引0 中,下一個在索引1 中,以此類推。

fn main() {
    let father_name = "Vlad";
    let son_name = "Adrian Fahrenheit";
    let family_name = "Țepeș";
    println!("This is {1} {2}, son of {0} {2}.", father_name, son_name, family_name);
}

father_name 在位置0,son_name 在位置1,family_name 在位置2。所以它印出的是 This is Adrian Fahrenheit Țepeș, son of Vlad Țepeș

也許你有一個非常複雜的字串要列印,有太多的變數要放在 {} 括號內。或者你需要印同一個變數不止一次。那麼在 {} 裡加上變數名就幫得上忙:

fn main() {
    println!(
        "{city1} is in {country} and {city2} is also in {country},
but {city3} is not in {country}.",
        city1 = "Seoul",
        city2 = "Busan",
        city3 = "Tokyo",
        country = "Korea"
    );
}

這樣會印出:

Seoul is in Korea and Busan is also in Korea,
but Tokyo is not in Korea.

在Rust中也可以進行非常複雜的列印,如果你想的話。這裡看到它是如何做到的:

{variable:padding alignment minimum.maximum}

要理解這個語法,看以下規則

  1. 你想要有變數名嗎?先寫出來,就像我們上面寫 {country} 一樣。 (如果你想做更多事,就在後面加一個 :)
  2. 你想要用填充字元嗎?例如,55 加上三個 "填充零" 就像 00055。
  3. 填充的對齊方式(左/中/右)?
  4. 你想要有最小長度嗎?(寫數字就行)
  5. 你想要有最大長度嗎?(寫數字,前面有個.)

例如,我想寫 "a",在它左邊有五個 ㅎ,在它右邊有五個 ㅎ:

fn main() {
    let letter = "a";
    println!("{:ㅎ^11}", letter);
}

這印出來是 ㅎㅎㅎㅎㅎaㅎㅎㅎㅎㅎ。我們看看 1) 到 5) 怎麼解釋這個情況,就能明白編譯器是怎麼解讀的:

  • 你要不要變數名?{:ㅎ^11} 沒有變數名。: 之前沒有任何內容。
  • 你需要填充字元嗎? {:ㅎ^11} 是。ㅎ 在 : 後面,還有一個 ^< 表示變數在填充字元左邊,> 表示在填充字元右邊,^ 表示在填充字元中間。
  • 要不要設定最小長度?{:ㅎ^11} 是:後面有一個 11。
  • 要不要設定最大長度?{:ㅎ^11} 不是:前面沒有.的數字。

下面是許多種型別格式化的例子:

fn main() {
    let title = "TODAY'S NEWS";
    println!("{:-^30}", title); // 沒變數名, 用-填充, 放中間, 30個字元長
    let bar = "|";
    println!("{: <15}{: >15}", bar, bar); // 沒變數名, 用空白填充, 各是15個字元長, 一左一右
    let a = "SEOUL";
    let b = "TOKYO";
    println!("{city1:-<15}{city2:->15}", city1 = a, city2 = b); // 變數city1和city2, 用-填充, 一左一右
}

印出:

---------TODAY'S NEWS---------
|                            |
SEOUL--------------------TOKYO