字符串

Rust 中有两种字符串类型:String&str

String 被存储为由字节组成的 vector(Vec<u8>),但保证了它一定是一个有效的 UTF-8 序列。String 是堆分配的,可增长的,且不是零结尾的(null terminated)。

&str 是一个总是指向有效 UTF-8 序列的切片(&[u8]),并可用来查看 String 的内容,就如同 &[T]Vec<T> 的全部或部分引用。

  1. fn main() {
  2. // (所有的类型标注都不是必需的)
  3. // 一个对只读内存中分配的字符串的引用
  4. let pangram: &'static str = "the quick brown fox jumps over the lazy dog";
  5. println!("Pangram: {}", pangram);
  6. // 逆序迭代单词,这里并没有分配新的字符串
  7. println!("Words in reverse");
  8. for word in pangram.split_whitespace().rev() {
  9. println!("> {}", word);
  10. }
  11. // 复制字符到一个 vector,排序并移除重复值
  12. let mut chars: Vec<char> = pangram.chars().collect();
  13. chars.sort();
  14. chars.dedup();
  15. // 创建一个空的且可增长的 `String`
  16. let mut string = String::new();
  17. for c in chars {
  18. // 在字符串的尾部插入一个字符
  19. string.push(c);
  20. // 在字符串尾部插入一个字符串
  21. string.push_str(", ");
  22. }
  23. // 这个缩短的字符串是原字符串的一个切片,所以没有执行新的分配操作
  24. let chars_to_trim: &[char] = &[' ', ','];
  25. let trimmed_str: &str = string.trim_matches(chars_to_trim);
  26. println!("Used characters: {}", trimmed_str);
  27. // 堆分配一个字符串
  28. let alice = String::from("I like dogs");
  29. // 分配新内存并存储修改过的字符串
  30. let bob: String = alice.replace("dog", "cat");
  31. println!("Alice says: {}", alice);
  32. println!("Bob says: {}", bob);
  33. }

更多 str/String 方法可以在 std::strstd::string 模块中 找到。

字面量与转义字符

书写含有特殊字符的字符串字面量有很多种方法。它们都会产生类似的 &str,所以最好 选择最方便的写法。类似地,字节串(byte string)字面量也有多种写法,它们都会产生 &[u8; N] 类型。

通常特殊字符是使用反斜杠字符 \ 来转义的,这样你就可以在字符串中写入各种各样的 字符,甚至是不可打印的字符以及你不知道如何输入的字符。如果你需要反斜杠字符,再用 另一个反斜杠来转义它就可以,像这样:\\

字面量中出现的字符串或字符定界符必须转义:"\""'\''

  1. fn main() {
  2. // 通过转义,可以用十六进制值来表示字节。
  3. let byte_escape = "I'm writing \x52\x75\x73\x74!";
  4. println!("What are you doing\x3F (\\x3F means ?) {}", byte_escape);
  5. // 也可以使用 Unicode 码位表示。
  6. let unicode_codepoint = "\u{211D}";
  7. let character_name = "\"DOUBLE-STRUCK CAPITAL R\"";
  8. println!("Unicode character {} (U+211D) is called {}",
  9. unicode_codepoint, character_name );
  10. let long_string = "String literals
  11. can span multiple lines.
  12. The linebreak and indentation here ->\
  13. <- can be escaped too!";
  14. println!("{}", long_string);
  15. }

有时会有太多需要转义的字符,或者是直接原样写出会更便利。这时可以使用原始字符 串(raw string)。

  1. fn main() {
  2. let raw_str = r"Escapes don't work here: \x3F \u{211D}";
  3. println!("{}", raw_str);
  4. // 如果你要在原始字符串中写引号,请在两边加一对 #
  5. let quotes = r#"And then I said: "There is no escape!""#;
  6. println!("{}", quotes);
  7. // 如果字符串中需要写 "#,那就在定界符中使用更多的 #。
  8. // 可使用的 # 的数目没有限制。
  9. let longer_delimiter = r###"A string with "# in it. And even "##!"###;
  10. println!("{}", longer_delimiter);
  11. }

想要非 UTF-8 字符串(记住,&strString 都必须是合法的 UTF-8 序列),或者 需要一个字节数组,其中大部分是文本?请使用字节串(byte string)!

  1. use std::str;
  2. fn main() {
  3. // 注意这并不是一个 &str
  4. let bytestring: &[u8; 20] = b"this is a bytestring";
  5. // 字节串没有实现 Display,所以它们的打印功能有些受限
  6. println!("A bytestring: {:?}", bytestring);
  7. // 字节串可以使用单字节的转义字符...
  8. let escaped = b"\x52\x75\x73\x74 as bytes";
  9. // ...但不能使用 Unicode 转义字符
  10. // let escaped = b"\u{211D} is not allowed";
  11. println!("Some escaped bytes: {:?}", escaped);
  12. // 原始字节串和原始字符串的写法一样
  13. let raw_bytestring = br"\u{211D} is not escaped here";
  14. println!("{:?}", raw_bytestring);
  15. // 把字节串转换为 &str 可能失败
  16. if let Ok(my_str) = str::from_utf8(raw_bytestring) {
  17. println!("And the same as text: '{}'", my_str);
  18. }
  19. let quotes = br#"You can also use "fancier" formatting, \
  20. like with normal raw strings"#;
  21. // 字节串可以不使用 UTF-8 编码
  22. let shift_jis = b"\x82\xe6\x82\xa8\x82\xb1\x82"; // SHIFT-JIS 编码的 "ようこそ"
  23. // 但这样的话它们就无法转换成 &str 了
  24. match str::from_utf8(shift_jis) {
  25. Ok(my_str) => println!("Conversion successful: '{}'", my_str),
  26. Err(e) => println!("Conversion failed: {:?}", e),
  27. };
  28. }

若需要在编码间转换,请使用 encoding crate。

Rust 参考中的 Tokens 一章详细地列出了书写字符串字面量和转义字符的方法。