@ Rust String的结构
Rust -> String
最近在学习Rust,对于语言什么的就不无脑吹了,毕竟任何语言都有优缺点,和其使用场景,存在即合理。
一个小坑
在学习Rust 时,没有去仔细研究语法,所以在实编程中遇到了问题,关于String 赋值的问题
先看个小例子
整形变量之间的赋值
let a = 10; // 给a 绑定一个值 10
let b = a; // 将b 绑定 a
println!("a = {}",a);
println!("b = {}",b);
b = 1;
println!("new b = {}",a);
println!("new a = {}",b);
输出结果
Finished dev [unoptimized + debuginfo] target(s) in 0.56s
Running `target/debug/hello-rust`
a = 10
b = 10
new b = 10
new a = 1
输出结果表明赋值成功,切a,b 变成了两个不同的值
字符串之间的赋值
let s1 = String::from("hello");
let s2 = s1;
println!("string s1 = {}",s1); // 这里会报错
println!("string s2 = {}",s2);
上述代码在编译时会报错
println!(“string s1 = {}”, s1);
/~~~~~~~~~~~~~~~~/ ^^value borrowed here after move
报错的原因我们下面分析
String 在rust中的结构
pub struct String {
vec: Vec<u8>,
}
.....
pub struct Vec<T> {
buf: RawVec<T>,
len: usize,
}
.....
pub struct RawVec<T, A: AllocRef = Global> {
ptr: Unique<T>,
cap: usize,
alloc: A,
}
上面就是构成 Rust ::String s1 的结构体
看不明白没关系 我们借用一张图去精简一下
上面就是S1 的结构:概括一下 String 拥有三个属性
- 长度(len)
- 容量(capacity)
- 指向字节内容的指针(ptr)
一般我们会理解:当我们将 s1 赋值给 s2,String 的数据被复制了,这意味着我们从栈上拷贝了它的指针、长度和容量。我们并没有复制指针指向的堆上数据。
这个理解是没有错误的。所以现在 s1 与s2 的关系应该是下图:
但是
在rust中有一个很重要的回收机制:
变量离开作用域后,Rust 自动调用 drop 函数并清理变量的堆内存。
这就有了一个问题:当 s2 和 s1 离开作用域,他们都会尝试释放相同的内存。这是一个叫做二次释放(double free)的错误,会导致内存安全性 bug。两次释放(相同)内存会导致内存污染,它可能会导致潜在的安全漏洞。 为了确保内存安全,这种场景下 Rust的处理则认为 s1 不再有效,因此 Rust 不需要在 s1 离开作用域后清理任何东西
所以就s1就被move
了
这种操作并不是浅拷贝,在rust中称为 move
,
可以理解为:s1 的String 被移动到了s2中
这里还有一个隐藏的设计原则:
Rust 永远也不会自动创建数据的 “深拷贝”。因此,任何 自动 的复制可以被认为对运行时性能影响较小
参考文章:
https://doc.rust-lang.org/book/ch03-05-control-flow.html