cover image
🚬

关于rust中字符串的一些理解

ruststr

Friday, April 21, 2023

这是很久之前使用vitess搭建的博客写的一篇文章
rust中有两种字符串,一种是&str一种是String。

rust

let s:&str = "hello world";let s:String = s.to_string();
下面说说我对于这两种字符串的理解。
&str可以叫做string slice或者字符串字面量,在说字符串字面量之前,得先了解一下什么是切片(slice):
切片(Slice)是对数据值的部分引用。 切片这个名字往往出现在生物课上,我们做样本玻片的时候要从生物体上获取切片,以供在显微镜上观察。在 Rust 中,切片的意思大致也是这样,只不过它从数据取材引用。
我在学习golang时才了解slice的概念,和golang不同,rust中的slice是不能改变的。可以这么理解,&str就是str的不可变借用,不可变借用当然是不能改变的了。Rust字符串内部存储的是一个u8数组,但是这个数组是Unicode字符经过UTF-8编码得来的。而数组本身是存储在stack上的不可变的数据类型,所以&str实际上切的是数组的一部分。并且&str是没有容量的(capacity)的,只有String才有,让我们用如下String的内存图分析:

rust

let my_name = "Pascal".to_string();
my_name的内存图:
								buffer
                   /   capacity
                 /   /  length
               /   /   /
            +–––+–––+–––+
stack frame │ • │ 8 │ 6 │ <- my_name: String
            +–│–+–––+–––+
              │
            [–│–––––––– capacity –––––––––––]
              │
            +–V–+–––+–––+–––+–––+–––+–––+–––+
       heap │ P │ a │ s │ c │ a │ l │   │   │
            +–––+–––+–––+–––+–––+–––+–––+–––+

            [––––––– length ––––––––]

my_name.push_str( " Precht");
let last_name = &my_name[7..];

				my_name: String   last_name: &str
            [––––––––––––]    [–––––––]
            +–––+––––+––––+–––+–––+–––+
stack frame │ • │ 16 │ 13 │   │ • │ 6 │ 
            +–│–+––––+––––+–––+–│–+–––+
              │                 │
              │                 +–––––––––+
              │                           │
              │                           │
              │                         [–│––––––– str –––––––––]
            +–V–+–––+–––+–––+–––+–––+–––+–V–+–––+–––+–––+–––+–––+–––+–––+–––+
       heap │ P │ a │ s │ c │ a │ l │   │ P │ r │ e │ c │ h │ t │   │   │   │
            +–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+–––+
可以看到my_name实际的数据是在heap上存储的,在stack中有存储了三个信息,一个指向heap的指针,一个capacity,还有一个length,在往下看,可以看到last_name在栈上面没有存储容量的信息。
再看看使用字符串字面量创建的字符串内存图:

rust

let my_name = "Pascal Precht";

						my_name: &str
            [–––––––––––]
            +–––+–––+
stack frame │ • │ 6 │ 
            +–│–+–––+
              │                 
              +––+                
                 │
 preallocated  +–V–+–––+–––+–––+–––+–––+
 read-only     │ P │ a │ s │ c │ a │ l │
 memory        +–––+–––+–––+–––+–––+–––+
现在有个问题是如果一个slice是引用了其他的拥有者的,那么,如上所示,使用字面量建立的字符串,它的所有者是谁(谁拥有所有权?)。
其实字符串字面量是比较特别的,它引用的一部分存储在只读的预分配内存中,也就是说不依赖堆中分配的缓冲区。可以看到在执行程序时,stack上仍然有一个指向内存的指针。
最后
之前学习java,golang以及dart的时候,有些知识点当时是模糊不清的,但是又没有记录下来,导致回头去找问题的时候浪费了很多时间,所以这次学习rust,记录一下在学习过程中的一些疑问以及自己对于这些疑问的思考🤫,方便日后查看。