字串
Rust 的字串主要型別有兩類:String 和 &str。有什麼差別呢?
&str是種簡單的字串。當你寫let my_variable = "Hello, world!"時,你建立的是一個&str。&str建立非常快。String是比較復雜的字串。它比較慢一點,但它有更多的功能。String是一個指標,資料在堆積上。
另外注意,&str 前面有 &,因為你需要一個參考來使用 str。這是因為我們先前看到的原因:堆疊需要知道資料大小。所以我們給它一個它知道大小的 &,然後它就滿意了。另外,因為你是用 & 去和 str 互動,你並不擁有它。但是 String 是一個 擁有所有權 的型別。我們很快就會知道為什麼這一點很重要。
&str 和String 都是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") 是來自 &str 的 String。但是對於 .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 了。