Rust中&str与&String辨析

tianyi Lv3

本篇将介绍Rust中字符串,&str与&String辨析和其最佳实践。

字符串

当 Rust 用户提到字符串时,往往指的就是 String 类型和&str字符串切片类型,这两个类型都是 UTF-8 编码。二者生成方式各有不同。

  • String
    1
    String::from("hello,world");
  • &str
    实际上字符串字面量既是&str,即使切片
    1
    let s: &str = "Hello, world!";

二者之间如何转化呢。

  • &str 类型生成 String 类型
    1
    2
    3
    String::from("hello,world")
    "hello,world".to_string()
    "hello,world".to_owned()
  • String 类型转为 &str
    这个也很简单,取引用及可。之所以这样可行,是因为 deref 隐式强制转换 (Rust 有一个机制叫做 Deref coercion(自动解引用转换),允许你将 &String 自动转换成 &str,因为 String 实现了 Deref<Target = str>。)
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    fn main() {
    let s = String::from("hello,world!");
    say_hello(&s);
    say_hello(&s[..]);
    say_hello(s.as_str());
    }

    fn say_hello(s: &str) {
    println!("{}",s);
    }

操作字符串

这里API所操作的字符串,类型都是String,因为String是可变字符串,这里简单介绍几个

push

在字符串尾部可以使用 push() 方法追加字符 char,也可以使用 push_str() 方法追加字符串字面量。这两个方法都是在原有的字符串上追加,并不会返回新的字符串。由于字符串追加操作要修改原来的字符串,则该字符串必须是可变的,即字符串变量必须由 mut 关键字修饰

1
2
3
4
5
6
7
8
9
fn main() {
let mut s = String::from("Hello ");

s.push_str("rust");
println!("追加字符串 push_str() -> {}", s);

s.push('!');
println!("追加字符 push() -> {}", s);
}

insert

可以使用 insert() 方法插入单个字符 char,也可以使用 insert_str() 方法插入字符串字面量,与 push() 方法不同,这俩方法需要传入两个参数,第一个参数是字符(串)插入位置的索引,第二个参数是要插入的字符(串),索引从 0 开始计数,如果越界则会发生错误。由于字符串插入操作要修改原来的字符串,则该字符串必须是可变的,即字符串变量必须由 mut 关键字修饰

1
2
3
4
5
6
7
fn main() {
let mut s = String::from("Hello rust!");
s.insert(5, ',');
println!("插入字符 insert() -> {}", s);
s.insert_str(6, " I like");
println!("插入字符串 insert_str() -> {}", s);
}

此外还有替换 (Replace),删除 (Delete,与字符串删除相关的方法有 4 个,它们分别是 pop(),remove(),truncate(),clear()。这四个方法仅适用于 String 类型。),连接 (Concatenate)

最佳实践

尽管有这么多API来操作字符串,尤其是日常开发中,我们经常可以在可变的String上使用push和push_str方法来建立字符串,或者使用其+操作符。 然而,使用format!往往更方便,特别是在有字面和非字面字符串混合的地方。

1
2
3
4
5
6
7
8
9
10
11
12
13
#![allow(unused)]
fn main() {
fn say_hello(name: &str) -> String {
// We could construct the result string manually.
// let mut result = "Hello ".to_owned();
// result.push_str(name);
// result.push('!');
// result

// But using format! is better.
format!("Hello {}!", name)
}
}

到底该怎么使用&str和&String

事实上,你应该总是倾向于使用借用类型而不是借用所有类型。 例如&str而不是&String,&[T]而不是&Vec,以及&T而不是&Box

我们希望确定一个词是否包含三个连续的元音。我们不需要拥有字符串来确定这一点,所以我们将使用一个引用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
fn three_vowels(word: &String) -> bool {
let mut vowel_count = 0;
for c in word.chars() {
match c {
'a' | 'e' | 'i' | 'o' | 'u' => {
vowel_count += 1;
if vowel_count >= 3 {
return true
}
}
_ => vowel_count = 0
}
}
false
}

fn main() {
let ferris = "Ferris".to_string();
let curious = "Curious".to_string();
println!("{}: {}", ferris, three_vowels(&ferris));
println!("{}: {}", curious, three_vowels(&curious));

// This works fine, but the following two lines would fail:
// println!("Ferris: {}", three_vowels("Ferris"));
// println!("Curious: {}", three_vowels("Curious"));

}

如果我们在最后两行取消注释,这个例子就会失败,因为&str类型不会被强制变成&String类型
例如,如果我们把我们的函数声明改成:

1
fn three_vowels(word: &str) -> bool {}

那么这两个版本都会编译并打印相同的输出。

1
2
Ferris: false
Curious: true

关键在于就是,&String 自动解引用为 &str。所以我们总是采取借用类型作为函数的参数。

  • Title: Rust中&str与&String辨析
  • Author: tianyi
  • Created at : 2025-08-05 10:33:18
  • Updated at : 2025-08-05 11:31:36
  • Link: https://github.com/ztygod/2025/08/05/Rust中-str与-String辨析/
  • License: This work is licensed under CC BY-NC-SA 4.0.
Comments