字符串 - Rust By Example
Description: Rust by Example (RBE) is a collection of runnable examples that illustrate various Rust concepts and standard libraries.

## Keyboard shortcuts

Press `←` or `→` to navigate between chapters

Press `S` or `/` to search in the book

Press `?` to show this help

Press `Esc` to hide this help

# 字符串

Rust 中最常用的两种字符串类型是 `String` 和 `&str`。

`String` 存储为字节向量（`Vec<u8>`），但保证始终是有效的 UTF-8 序列。`String` 在堆上分配，可增长，且不以 null 结尾。

`&str` 是一个切片（`&[u8]`），它始终指向一个有效的 UTF-8 序列，可以用来查看 `String`，就像 `&[T]` 是 `Vec<T>` 的一个视图一样。

    fn main() {
        // （所有的类型注解都不是必须的）
        // 一个指向只读内存中分配的字符串的引用
        let pangram: &'static str = "敏捷的棕色狐狸跳过懒惰的狗";
        println!("全字母句：{}", pangram);

        // 反向遍历单词，不会分配新的字符串
        println!("单词逆序");
        for word in pangram.split_whitespace().rev() {
            println!("> {}", word);
        }

        // 将字符复制到向量中，排序并去重
        let mut chars: Vec<char> = pangram.chars().collect();
        chars.sort();
        chars.dedup();

        // 创建一个空的、可增长的 `String`
        let mut string = String::new();
        for c in chars {
            // 在字符串末尾插入一个字符
            string.push(c);
            // 在字符串末尾追加另一个字符串
            string.push_str(", ");
        }

        // 修剪后的字符串是原始字符串的一个切片，因此不会进行新的内存分配
        let chars_to_trim: &[char] = &[' ', ','];
        let trimmed_str: &str = string.trim_matches(chars_to_trim);
        println!("使用的字符：{}", trimmed_str);

        // 在堆上分配一个字符串
        let alice = String::from("我喜欢狗");
        // 分配新的内存并在其中存储修改后的字符串
        let bob: String = alice.replace("狗", "猫");

        println!("爱丽丝说：{}", alice);
        println!("鲍勃说：{}", bob);
    }

更多 `str` 和 `String` 的方法可以在 std::str 和 std::string 模块中找到

## 字面值和转义字符

有多种方式可以编写包含特殊字符的字符串字面值。所有方式都会产生类似的 `&str`，因此最好使用最方便编写的形式。同样，也有多种方式可以编写字节字符串字面值，它们都会产生 `&[u8; N]` 类型。

通常，特殊字符用反斜杠（`\`）进行转义。这样你可以在字符串中添加任何字符，包括不可打印的字符和你不知道如何输入的字符。如果你想要一个字面的反斜杠，用另一个反斜杠转义它：`\\`

出现在字面值内的字符串或字符字面值分隔符必须被转义：`"\""` 和 `'\''`。

    fn main() {
        // 你可以使用转义来通过十六进制值写入字节...
        let byte_escape = "我正在写 \x52\x75\x73\x74！";
        println!("你在做什么\x3F（\\x3F 表示 ?）{}", byte_escape);

        // ...或 Unicode 码点。
        let unicode_codepoint = "\u{211D}";
        let character_name = "\"双线大写 R\"";

        println!("Unicode 字符 {} (U+211D) 被称为 {}",
                    unicode_codepoint, character_name );

        let long_string = "字符串字面值
                            可以跨越多行。
                            这里的换行和缩进 ->\
                            <- 也可以被转义！";
        println!("{}", long_string);
    }

有时需要转义的字符太多，或者直接按原样写出字符串会更方便。这时就可以使用原始字符串字面值。

    fn main() {
        let raw_str = r"转义在这里不起作用：\x3F \u{211D}";
        println!("{}", raw_str);

        // 如果你需要在原始字符串中使用引号，可以添加一对 #
        let quotes = r#"然后我说："无处可逃！""#;
        println!("{}", quotes);

        // 如果你需要在字符串中使用 "#，只需在分隔符中使用更多的 #。
        // 你最多可以使用 255 个 #。
        let longer_delimiter = r###"一个包含 "# 的字符串。甚至还有 "##！"###;
        println!("{}", longer_delimiter);
    }

想要一个非 UTF-8 的字符串吗？（请记住，`str` 和 `String` 必须是有效的 UTF-8）。或者你想要一个主要是文本的字节数组？字节字符串来帮忙！

    use std::str;

    fn main() {
        // 注意，这实际上不是一个 `&str`
        let bytestring: &[u8; 21] = b"这是一个字节字符串";

        // 字节数组没有 `Display` trait，所以打印它们有一些限制
        println!("一个字节字符串：{:?}", bytestring);

        // 字节字符串可以包含字节转义...
        let escaped = b"\x52\x75\x73\x74 作为字节";
        // ...但不允许 Unicode 转义
        // let escaped = b"\u{211D} 是不允许的";
        println!("一些转义的字节：{:?}", escaped);

        // 原始字节字符串的工作方式与原始字符串相同
        let raw_bytestring = br"\u{211D} 在这里不会被转义";
        println!("{:?}", raw_bytestring);

        // 将字节数组转换为 `str` 可能会失败
        if let Ok(my_str) = str::from_utf8(raw_bytestring) {
            println!("作为文本的相同内容：'{}'", my_str);
        }

        let _quotes = br#"你也可以使用"更花哨的"格式，\
                        就像普通的原始字符串一样"#;

        // 字节字符串不必是 UTF-8 编码
        let shift_jis = b"\x82\xe6\x82\xa8\x82\xb1\x82\xbb"; // SHIFT-JIS 编码的 "ようこそ"

        // 但它们并不总是能被转换为 `str`
        match str::from_utf8(shift_jis) {
            Ok(my_str) => println!("转换成功：'{}'", my_str),
            Err(e) => println!("转换失败：{:?}", e),
        };
    }

对于字符编码之间的转换，请查看 encoding crate。

关于编写字符串字面值和转义字符的更详细说明，请参阅 Rust 参考手册的「标记」章节。