Skip to content

字符串

Rust 字符串的分多种:

  1. 语言级别 str,通常以引用类型出现 &str 具备所有权,且 可变(无法被修改);
  2. 标准库的实现,如:
    • 使用最广泛的 String具有所有权、且 可变
    • 还有 OsStringOsStrCsStringCsStr 这些。

字符串字面量是切片引用。

也就是说:

let s1 = "Hello, Boii!"; // s1 类型为 &str

等价于:

let s1: &str = "Hello, Boii!";


Rust 中字符是 Unicode 编码,每个字符固定占 4 个字节。但是字符串是 UTF-8 编码,这是一种变长编码,字符串的每个字符所占字节数在 1~4 字节之间。


String 与 str 转换

  • String 转 str,取引用即可

    1
    2
    3
    4
    5
    6
    fn main() {
        let s: String = String::from("Hello, Boii");
        let s1: &str = &s;         // 第一种方式
        let s2: &str = &s[..];     // 第二种方式
        let s3: &str = s.as_str(); // 第三种方式
    }
    
    这种灵活的用法是因为 Deref 这个 trait 隐式强制转换了。

  • str 转 String

    1
    2
    3
    4
    fn main() {
        let s1: String = "Hello, Boii".to_string();     // 第一种方式
        let s2: String = String::from("Hello, Boii");   // 第二种方式
    }
    

字符串操作

追加 push、push_str

  • 在原有字符串上追加,要求 可变,所以字符串变量需加 mut 关键字。

1
2
3
4
5
6
let mut s = String::from("Hello, Boii");
s.push('!');
println!("追加字符 push() -> {}", s);

s.push_str(" and Eva");
println!("追加字符串 push_str() -> {}", s);
Output:
追加字符 push() -> Hello, Boii!
追加字符串 push_str() -> Hello, Boii! and Eva

插入 insert、insert_str

  • 在原有字符串上追加,要求 可变,所以字符串变量需加 mut 关键字。

1
2
3
4
5
6
let mut s = String::from("Hello, Boii");
s.insert(5, '~');
println!("插入字符 insert() -> {}", s);

s.insert_str(8, " Mr. ");
println!("插入字符串 insert_str() -> {}", s);
Output:
插入字符 insert() -> Hello~, Boii
插入字符串 insert_str() -> Hello~, Mr. Boii

替换 replace、replacen、replace_range

  1. replace(old, new),

    • 该方法会替换 所有 匹配到的字符串;
    • 返回 新字符串 要求可变

    1
    2
    3
    let s = String::from("I like rust. rust is the best language.");
    let s1 = s.replace("rust", "RUST")
    dbg!(s1);
    
    Output:
    s1 = "I like RUST. RUST is the best language."
    

  2. replacen(old, new, num)

    • 该方法会替换 前 num 个 匹配到的字符串;
    • 返回 新字符串 要求可变

    1
    2
    3
    let s = String::from("I like rust. rust is the best language.");
    let s1 = s.replace("rust", "RUST", 1)
    dbg!(s1);
    
    Output:
    s1 = "I like RUST. rust is the best language."
    

  3. replace_range(range, new)

    • 该方法会将指定的范围内的字符替换成新字符串;
    • 原有字符串 上修改,要求可变,所以字符串变量需加 mut 关键字。

    1
    2
    3
    4
    5
    6
    let mut s = String::from("I like rust. rust is the best language.");
    s.replace_range(7..11, "Golang");
    dbg!(&s);
    
    s.replace_range(7.., "Rust");
    dbg!(&s);
    
    Output:
    &s = "I like Golang. rust is the best language."
    &s = "I like Rust"
    

删除 pop、delete、truncate、clear

  1. pop()

    • 删除 并 返回 被删除的字符
    • 原有字符串 上修改,要求可变,所以字符串变量需加 mut 关键字;
    • 因为有返回值,为 Option 类型,如果字符串为空则返回 None。

    1
    2
    3
    4
    5
    6
    7
    let mut s = String::from("Rust pop 中文!");
    let p1 = s.pop();
    let p2 = s.pop();
    
    dbg!(p1);
    dbg!(p2);
    dbg!(s);
    
    Output:
    1
    2
    3
    4
    5
    6
    7
    p1 = Some(
        '!',
    )
    p2 = Some(
        '文',
    )
    s = "Rust pop 中"
    

  2. remove(index)

    • 删除 并 返回字符串的 被删除的字符
    • 原有字符串 上修改,要求可变,所以字符串变量需加 mut 关键字;
    • 因为有返回值,为 char 类型;如果字符串为空串,调用 remove 会发生运行时错误。
    • 如果 index 不是合法的字符边界,会发生错误。

    1
    2
    3
    4
    5
    6
    7
    8
    let mut s = String::from("测试一下 remove 方法");
    let p1 = s.remove(0);
    // let p2 = s.remove(1);    // 测是中文,占 3 个字节 0, 1, 2, 所以 1 不是合法的字符边界,这里会报错
    let p3 = s.remove(3);
    
    dbg!(p1);
    dbg!(p3);
    dbg!(s);
    
    Output:
    1
    2
    3
    p1 = '测'
    p3 = '一'
    s = "试下 remove 方法"
    

  3. truncate(index)

    • 删除字符串中 从指定位置开始到结尾的全部字符
    • 原有字符串 上修改,要求可变,所以字符串变量需加 mut 关键字;
    • 没有返回值;如果字符串为空串,也不会发生错误。
    • 如果 index 不是合法的字符边界,会发生错误。

    1
    2
    3
    let mut s = String::from("测试 truncate");
    s.truncate(3);
    dbg!(s);
    
    Output:
    s = "测"
    

  4. clear()

    • 清空字符串;
    • 原有字符串 上修改,要求可变,所以字符串变量需加 mut 关键字;
    • 相当于 truncate(0).

    1
    2
    3
    let s = String::from("clear string");
    s.clear();
    dbg!(s);
    
    Output:
    s = ""
    

连接字符串

  1. 使用 ++= 连接

    使用 + 操作符时相当于调用 std::string 标准库中的 add() 方法,所以要求第二个参数(即 + 操作符右边的变量)为 &str 类型。

    不过 ++= 都是返回一个 新字符串

    1
    2
    3
    4
    5
    6
    7
    8
    let s1 = String::from("Hello");
    let s2 = String::from("World!");
    let s3 = s1 + &s2;
    
    let mut s4 = s3 + "!";
    s4 += "~~~";
    
    println!("{}", s4);
    
    Output:
    HelloWorld!!~~~
    
    2. 使用 format!() 连接 format!() 方式适用于 String&str,用法与 println!() 类似。

    1
    2
    3
    4
    5
    let s1 = String::from("Hello");
    let s2 = "Boii!";
    let s  = format!("{} {}", s1, s2);
    
    println!("{}", s);
    
    Output:
    Hello Boii!
    

字符串转义

Rust 可以用过转义的方式 \ 输出 ASCII 和 Unicode 字符。

1
2
3
4
fn main() {
    let bs = "I am learning about \x52\x75\x73\x74!"
    println!("{}", bs);
}

Output:

I am learning about Rust!
1
2
3
4
fn main() {
    let uc = "\u{211D}";
    println!("Unicode character {} (U+211D) is called DOUBLE-STRUCK CAPITAL R", uc);
}

Output:

Unicode character ℝ (U+211D) is called DOUBLE-STRUCK CAPITAL R
1
2
3
4
5
6
7
fn main() {
    let long_string = "String literals
                        can span multiple lines.
                        The linebreak and indentation here ->\
                        <- can be escaped too!";
}
println!("{}", long_string);

Output:

1
2
3
String literals
                        can span multiple lines.
                        The linebreak and indentation here -><- can be escaped too!

也可以取消转义,原样打印出来

1
2
3
4
fn main() {
    let s = "hello \\x52\\x75\\x73\\x74";
    println!("{}", s);
}

Output:

hello \x52\x75\x73\x74
fn main() {
    let s = r"Escapes don't work here: \x3F \u{211D}";
    println!("{}", s);

    // 如果字符串包含双引号,可以在开头和结尾加 #
    let quotes = r#" And then I said: "There is no escape!". "#;
    println!("{}", quotes);

    // 如果还是有歧义,可以继续增加,没有限制
    let longer_delimiter = r###" A string with "# in it. And even "##! "###;
    println!("{}", longer_delimiter);
}

Output:

1
2
3
Escapes don't work here: \x3F \u{211D}
 And then I said: "There is no escape!". 
 A string with "# in it. And even "##! 

操作 UTF-8 字符串

fn main() {
    let s = "中国话";
    for c in s.chars() {
        println!("{}", c);
    }

    let s2 = String::from("中国话");
    for c in s2.chars() {
        println!("{}", c);
    }
}

Output:

1
2
3
4
5
6
1
2
3
4
5
fn main() {
    for b in "中国人".bytes() {
        println!("{}", b);
    }
}

Output:

1
2
3
4
5
6
7
8
9
228
184
173
229
155
189
228
186
186