概念
Rust开发中直接使用裸指针并不常见,裸指针更多的用于标准库,和一些基础设施,数据结构等。
分类
Rust裸指针分两种
* mut T
可变裸指针* const T
不可变裸指针
这两者可以互相转换:这里似乎Rust并不介意我们对a存在多个可变裸指针。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
fn f1() {
let a = 100;
let b = &a as *const i32;
let c = b as *mut i32;
let d = c as *mut i32;
println!("b = {}", unsafe { *b });
println!("c = {}", unsafe { *c });
println!("d = {}", unsafe { *d });
}
-------
b = 100
c = 100
d = 100
如何获取一个裸指针
1. 由引用转换而来
1 |
|
也可以这样转:
1 |
|
侧面印证,引用本质就是指针
2. 由Box转换而来
这里会消费一个Box指针生成一个裸指针。
1 | struct A; |
功能
read
将裸指针转为裸指针类型对应的对象。
1 | fn main() { |
write
null/is_null
构建一个空指针/判断是否为空指针
1 | fn main() { |
特性
1. Drop裸指针不会释放其指向的元素
1 | struct A; |
执行这段代码并不会打印drop A。换句话说,释放裸指针并不会释放它所指向的对象。裸指针b的释放不会同时释放其指向的内存结构。
2. 构建裸指针safe,解引用裸指针是unsafe
比如我们从引用转裸指针,引用总是合法的指针,然而将其转为裸指针后。原本跟随引用的生命周期检查则失效了:
1 | fn main() { |
上例中,函数u执行完成后,裸指针y指向的元素已经回收。此时解引用y将会产出ub,这里使用debug可能仍能打印正确结果2
,使用release build会看到奇怪的问题。
3. 不能将value move出来
和引用一样不能从解引用(裸指针)move,只能读写解引用。
1 | struct A; |
那么read可以move吗?
1 | struct A; |
看起来没报错。我们看read这个函数的注释:
1 | /// Reads the value from `src` without moving it. This leaves the |
实际上 read 也不会move原数据。所以从语义上来看,将裸指针转为了裸指针对应的对象。src
must be [valid] for reads.
1 | /// |
rust read的语义这里Rust官方论坛有些讨论:
https://users.rust-lang.org/t/why-does-reading-a-raw-pointer-cause-a-drop/66411/2
语义上讲如果T是非Copy,则执行read就应该将原数据从旧地址move到新地址,实际上实现上根据文档来看是bit级浅拷贝
,原地址内存并没有动。
实现
应该说Rust裸指针和C的指针仍有非常大的不同。C指针只是一个地址。Rust裸指针兼顾部分安全性和效率。
1 | //从下面结构定义可以看到,裸指针本质就是PtrComponents<T> |
Thin 瘦指针Metadata是空的。此外就是metadata不为空的胖指针。str,切片,trait object是目前的三种常见的胖指针。
裸指针const T/ mut T将内存和类型系统相连接,*const T代表了一个内存块,指示了内存块首地址,大小,对齐等属性,以及后文提到的元数据,但不保证这个内存块的有效性和安全性。