字串
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 了。