15-字符串

字符串 — Easy Rust 中文译本

译文 · 基于 Easy Rust

原文链接: https://dhghomon.github.io/easy_rust/Chapter_14.html

中文参考:kumakichi/easy_rust_chs

字符串

Rust有两种主要类型的字符串。String和&str. 有什么区别呢?

  • &str是一个简单的字符串。当你写let my_variable = "Hello, world!"时,你会创建一个&str。&str是非常快的。
  • String是一个更复杂的字符串。它比较慢,但它有更多的功能。String是一个指针,数据在堆上。

另外注意,&str前面有&,因为你需要一个引用来使用str。这是因为我们上面看到的原因:堆需要知道大小。所以我们给它一个&,它知道大小,然后它就高兴了。另外,因为你用一个&与一个str交互,你并不拥有它。但是一个String是一个拥有的类型。我们很快就会知道为什么这一点很重要。

&str和String都是UTF-8。例如,你可以写

1
2
3
4
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。

1
2
3
4
fn main() {
    let name = "😂";
    println!("My name is actually {}", name);
}

在你的电脑上,会打印My name is actually 😂,除非你的命令行不能打印。那么它会显示My name is actually �。但Rust对emojis或其他Unicode没有问题。

我们再来看看str使用&的原因,以确保我们理解。

  • str是一个动态大小的类型(动态大小=大小可以不同)。比如 “서태지"和 “Adrian Fahrenheit Țepeș"这两个名字的大小是不一样的。
1
2
3
4
5
6
7
8
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ș"));
}

这个打印:

1
2
3
4
5
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!一样,只是它创建了一个字符串,而不是打印。所以你可以这样做:
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
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
    );
}

现在我们有了一个一起命名的字符串,但还没有打印出来。

还有一种创建String的方法叫做.into(),但它有点不同,因为.into()并不只是用来创建String。有些类型可以很容易地使用From和.into()转换为另一种类型,并从另一种类型转换出来。而如果你有From,那么你也有.into()。From 更加清晰,因为你已经知道了类型:你知道 String::from("Some str") 是一个来自 &str 的 String。但是对于.into(),有时候编译器并不知道。

1
2
3
fn main() {
    let my_string = "Try to make this a String".into(); // ⚠️
}

Rust不知道你要的是什么类型,因为很多类型都可以从一个&str创建出来。它说:“我可以把一个&str做成很多东西。你想要哪一种?”

1
2
3
4
5
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

所以你可以这样做:

1
2
3
fn main() {
    let my_string: String = "Try to make this a String".into();
}

现在你得到了一个字符串。

最后修改 August 21, 2026: 更新 (76fc81a2e)