赞
踩
到目前为止,Rust的设计让人觉得非常放心。利用类型系统消除空指针,简洁
明了的“唯一修改权”原则,消除了野指针,还有各种智能指针可以使用,甚至可以
利用同样的规则消除多线程环境下的数据竞争,这一切就像一组简洁的数学定理一
样,构建了一整套清晰的“内存安全”代码的“世界观”。
但是这只是Rust的一部分。还有一些情况,编译器的静态检查是不够用的,它
没办法自动推理出来这段代码究竟是不是安全的。这种时候,我们就需要使用
unsafe关键字来保证代码的安全性。
本章简单介绍一下Rust的unsafe代码。
unsafe关键字
Rust的unsafe关键字有以下几种用法:
·用于修饰函数fn;
·用于修饰代码块;
·用于修饰trait;
·用于修饰impl。
当一个fn是unsafe的时候,意味着我们在调用这个函数的时候需要非常小心。
它可能要求调用者满足一些其他的重要约束,而这些约束条件无法由编译器自动检
查来保证。有unsafe修饰的函数,要么使用unsafe语句块调用,要么在unsafe函数中
调用。因此需要注意,unsafe函数是具有“传递性”的,unsafe函数的“调用者”也必须
用unsafe修饰。
比如,String::from_raw_parts就是一个unsafe函数,它的签名如下:
pub unsafe fn from_raw_parts(buf: *mut u8, length: usize, capacity: usize) -> String
它之所以是unsafe的,是因为String类型对所有者有一个保证:它内部存储的是
合法的utf-8字符串。而这个函数没有检查传递进来的这个缓冲区是否满足这个条
件,所以使用者必须这样调用:
// 自己保证这个缓冲区包含的是合法的 utf-8 字符串
let s = unsafe { String::from_raw_parts(ptr as *mut _, len, capacity) } ;
上面这个写法就是unsafe代码块的用法。使用unsafe关键字包围起来的语句块,
里面可以做一些一般情况下做不了的事情。但是,它也是有规矩的。与普通代码比
起来,它多了以下几项能力:
·对裸指针执行解引用操作;
·读写可变静态变量;
·读union或者写union的非Copy成员;
·调用unsafe函数。
在Rust中,有些地方必须使用unsafe才能实现。比如标准库提供的一系列
intrinsic函数,很多都是unsafe的,再比如调用extern函数必须在unsafe中实现。另
外,一些重要的数据结构内部也使用了unsafe来实现一些功能。
当unsafe修饰一个trait的时候,那么意味着实现这个trait也需要使用unsafe。比
如在后面讲线程安全的时候会着重讲解的Send、Sync这两个trait。因为它们很重
要,是实现线程安全的根基,如果由程序员来告诉编译器,强制指定一个类型是否
满足Send、Sync,那么程序员自己必须很谨慎,必须很清楚地理解这两个trait代表
的含义,编译器是没有能力推理验证这个impl是否正确的。这种impl对程序的安全
性影响很大。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。