Rust String 中的move操作

@ 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

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值