字串

YouTube 上觀看本章內容

Rust 的字串主要型別有兩類:String&str。有什麼差別呢?

  • &str 是種簡單的字串。當你寫 let my_variable = "Hello, world!" 時,你建立的是一個 &str&str 建立非常快。
  • String 是比較復雜的字串。它比較慢一點,但它有更多的功能。String 是一個指標,資料在堆積上。

另外注意,&str 前面有 &,因為你需要一個參考來使用 str。這是因為我們先前看到的原因:堆疊需要知道資料大小。所以我們給它一個它知道大小的 &,然後它就滿意了。另外,因為你是用 & 去和 str 互動,你並不擁有它。但是 String 是一個 擁有所有權 的型別。我們很快就會知道為什麼這一點很重要。

&strString 都是UTF-8。例如,你可以寫:

fn main() {
    let name = "서태지"; // 這是韓國名字。沒問題,因為 &str 是 UTF-8。
    let other_name = String::from("Adrian Fahrenheit Țepeș"); // UTF-8 的 Ț 和 ș 沒問題。
}

你可以在 String::from("Adrian Fahrenheit Țepeș") 中看到,從 &str 中建立 String 很容易。這兩種型別雖然不同,但彼此聯繫非常緊密。

你甚至可以寫表情符號,這要感謝 UTF-8。

fn main() {
    let name = "😂";
    println!("My name is actually {}", name);
}

在你的電腦上,會印出 My name is actually 😂,除非你的命令列印不出(Unicode字元)。那麼它會顯示 My name is actually �。但 Rust 對 emojis 或其他 Unicode (處理上)沒有問題。

我們再來看看 str 使用 & 的原因,以確保我們有理解。

  • str 是一個動態大小(dynamically sized)的型別(動態大小 = 大小可以不同)。比如 "서태지" 和 "Adrian Fahrenheit Țepeș" 這兩個名字的大小是不一樣的:
fn main() {

    println!("A String is always {:?} bytes. It is Sized.", std::mem::size_of::<String>()); // std::mem::size_of::<Type>() 給你型別的位元組單位大小
    println!("And an i8 is always {:?} bytes. It is Sized.", std::mem::size_of::<i8>());
    println!("And an f64 is always {:?} bytes. It is Sized.", std::mem::size_of::<f64>());
    println!("But a &str? It can be anything. '서태지' is {:?} bytes. It is not Sized.", std::mem::size_of_val("서태지")); // std::mem::size_of_val() 給你變數的位元組單位大小
    println!("And 'Adrian Fahrenheit Țepeș' is {:?} bytes. It is not Sized.", std::mem::size_of_val("Adrian Fahrenheit Țepeș"));
}

列出:

A String is always 24 bytes. It is Sized.
And an i8 is always 1 bytes. It is Sized.
And an f64 is always 8 bytes. It is Sized.
But a &str? It can be anything. '서태지' is 9 bytes. It is not Sized.
And 'Adrian Fahrenheit Țepeș' is 25 bytes. It is not Sized.

這就是為什麼我們需要一個 &,因為 & 建立一個指標,而 Rust 知道指標的大小。所以指標會放在堆疊中。如果我們寫的是 str,Rust 因為不知道大小就不曉得該怎麼做了。

有很多方法可以建立 String。這裡是其中一些:

  • String::from("This is the string text"); 這是 String 型別用文字建立 String 的方法。
  • "This is the string text".to_string()。 這是 &str 型別用來做出 String 的方法。
  • format! 巨集。 像是 println!,只不過它是建立 String,而不是列印。所以你可以這樣做:
fn main() {
    let my_name = "Billybrobby";
    let my_country = "USA";
    let my_home = "Korea";

    let together = format!(
        "I am {} and I come from {} but I live in {}.",
        my_name, my_country, my_home
    );
}

現在我們有了名為 together 的 String,但還沒有印出來。

還有一種建立 String 的方法叫做 .into(),但它有點不同,因為 .into() 並不只是用來建立 String。有些型別可以很容易地使用 From.into() 來回轉換為另一種型別。而如果你有 From,那麼你也有 .into()From 更加清晰,因為你已經知道了型別:你知道 String::from("Some str") 是來自 &strString。但是對於 .into(),有時候編譯器並不知道:

fn main() {
    let my_string = "Try to make this a String".into(); // ⚠️
}

Rust 不知道你要的是什麼型別,因為很多型別都可以由 &str 來組成。它說:"我可以把 &str 變成很多東西。你想要哪一種?"

error[E0282]: type annotations needed
 --> src\main.rs:2:9
  |
2 |     let my_string = "Try to make this a String".into();
  |         ^^^^^^^^^ consider giving `my_string` a type

所以你可以這樣做:

fn main() {
    let my_string: String = "Try to make this a String".into();
}

現在你得到 String 了。