剖析Rust中的str和String之间的区别与共性

之前写代码的时候发现自己其实完全不懂Stringstr之间的区别,现在写篇文章来细 🔒

首先查看官方参考文档中对 String 的描述如下

The String type is the most common string type that has ownership over the contents of the string. It has a close relationship with its borrowed counterpart, the primitive str.

我们可以发现 String 是拥有字符串内容的所有权的,而 str 则被描述为“借用”。查看具体实现的源代码,有如下的实现内容:

1
2
3
pub struct String {
vec: Vec<u8>,
}

也就是说,String 是确确实实有“拥有”这个字符串内容的。而 str参考文档中被描述为如下的内容:

Unicode string slices.

See also the std::str primitive module.

The &str type is one of the two main string types, the other being String. Unlike its String counterpart, its contents are borrowed.

string slices 意为字符串切片,那么说明一般 str 类型是作为指针,描述一个字符串切片的存在,而作为一个 primitive type,无法找到其具体内容实现,但是可以确定的是,它并没有对指向内容的所有权,但是由于往往作为引用 &str 存在,因此有着更高的灵活性与便捷度。

关于字符常量

我们常常会使用类似于如下的代码创建 String 以及 &str 变量:

1
2
let a_string = String::from("驻留字符串");
let a_str = "这是字符串常量";

学习 Java 虚拟机时就学习过,虚拟机会将编译得到的字符串驻留(interned),即,提前为不可变字符串在全局区域分配空间,如果有多个相同引用只需要实际创建一次即可。

我们可以看到,不论是上面的 a_string 还是下面的 a_str,都使用了相应的字符串常量,分别分配了两段内容为驻留字符串这是字符串常量的文本于内存中,同时,String::from的实现为:

1
2
3
4
5
6
// String::from
impl From<Box<str>> for String {
fn from(s: Box<str>) -> String {
s.into_string()
}
}

继续追溯:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// str::into_string
pub fn into_string(self: Box<str>) -> String {
let slice = Box::<[u8]>::from(self);
unsafe { String::from_utf8_unchecked(slice.into_vec()) }
}
// String::from_utf8_unchecked
pub unsafe fn from_utf8_unchecked(bytes: Vec<u8>) -> String {
String { vec: bytes }
}
// str::into_vec
pub fn into_vec<A: Allocator>(self: Box<Self, A>) -> Vec<T, A> {
// N.B., see the `hack` module in this file for more details.
hack::into_vec(self)
}
// hack::into_vec
pub fn into_vec<T, A: Allocator>(b: Box<[T], A>) -> Vec<T, A> {
unsafe {
let len = b.len();
let (b, alloc) = Box::into_raw_with_allocator(b);
Vec::from_raw_parts_in(b as *mut T, len, len, alloc)
}
}

我们发现,String::from其实是根据字符串常量内容自行 alloc 了相同的空间,并且将内容复制到该空间内(在堆空间内)。

总结

对于希望拥有字符串所有权,需要更大程度地掌控字符串,请使用 String 类型,而如果是作为一个不会更改的常量存在,则应该使用 str 类型进行操作。

作者

Hyiker Hu

发布于

2021-03-05

更新于

2021-09-26

许可协议

评论