学做网站需要哪几本书,网站建设常用单词,湖南搜索引擎推广服务,深圳市城乡住房和建设局网站首页这篇文章简单介绍下unsafe rust的几个要点
1. 解引用裸指针
裸指针其实就是C或者说C的指针#xff0c;与C的指针不同的是#xff0c;Rust的裸指针还是要分为可变和不可变#xff0c;*const T 和 *mut T#xff1a;
基于引用创建裸指针 let mut num 5;let r1 num …这篇文章简单介绍下unsafe rust的几个要点
1. 解引用裸指针
裸指针其实就是C或者说C的指针与C的指针不同的是Rust的裸指针还是要分为可变和不可变*const T 和 *mut T
基于引用创建裸指针 let mut num 5;let r1 num as *const i32;let r2 mut num as *mut i32;或者不想用类型转换也可以这么写书上认为这是一种隐式转换我觉得就是一种类型声明 let r3: *const i32 num;let r4: *mut i32 mut num;创建裸指针是安全的行为而解引用裸指针才是不安全的行为
fn main() {let mut num 5;let r1 num as *const i32;unsafe {println!(r1 is: {}, *r1);}
}基于内存地址创建裸指针
基于内存地址创建裸指针相当于直接给指针赋值为某个内存地址
use std::{slice::from_raw_parts, str::from_utf8_unchecked};fn main() {let string bluebonnet27;//as_ptr: Converts a string slice to a raw pointer.let pointer_num string.as_ptr() as usize;let length string.len();unsafe {//from_raw_parts: Forms a slice from a pointer and a length.//from_utf8_unchecked: Converts a slice of bytes to a string slice without checking that the string contains valid UTF-8let res from_utf8_unchecked(from_raw_parts(pointer_num as *const u8, length));println!(The {} bytes at 0x{:X} stored: {},length, pointer_num, res)}
}我们可以尝试将pointer_num 和length改成其他值
基于智能指针创建裸指针
还有一种创建裸指针的方式那就是基于智能指针来创建:
let a: Boxi32 Box::new(10);
// 需要先解引用a
let b: *const i32 *a;
// 使用 into_raw 来创建
let c: *const i32 Box::into_raw(a);在C中也可以通过智能指针创建裸指针并且这种做法也存在一些问题。比如如下的代码
auto p make_sharedint(42);
int* iPtr p.get();
{shared_ptrint(iPtr);
}int value *p; // Error! 内存已经被释放p与iPtr指向了相同的内存然而通过get方法后将内存管理权转移给了普通指针。iPtr传递给里面程序块的临时智能指针后引用计数为1随后出了作用域减少为0释放内存。
2. 调用 unsafe 函数或方法
很简单加上unsafe的声明就行
unsafe fn dangerous() {}
fn main() {dangerous();
}这样是编译不过的因为dangerous是个unsafe函数。加上unsafe调用即可
unsafe fn dangerous() {}
fn main() {unsafe {dangerous();}
}借用官方文档的一句话“在整个代码库code base指构建一个软件系统所使用的全部代码中要尽可能减少不安全代码的量”比如我们上面的这个例子
fn main() {let string bluebonnet27;//as_ptr: Converts a string slice to a raw pointer.let pointer_num string.as_ptr() as usize;let length string.len();unsafe {//from_raw_parts: Forms a slice from a pointer and a length.//from_utf8_unchecked: Converts a slice of bytes to a string slice without checking that the string contains valid UTF-8let res from_utf8_unchecked(from_raw_parts(pointer_num as *const u8, length));println!(The {} bytes at 0x{:X} stored: {},length, pointer_num, res)}
}printlin!是个安全函数将它放在unsafe唯一的原因是我们需要在res的生命周期内打印它。所以我们可以改成这样
fn get_str(pointer_num: usize, length: usize) - String {unsafe {//from_raw_parts: Forms a slice from a pointer and a length.//from_utf8_unchecked: Converts a slice of bytes to a string slice without checking that the string contains valid UTF-8String::from(from_utf8_unchecked(from_raw_parts(pointer_num as *const u8,length,)))}
}fn main() {let string bluebonnet27;//as_ptr: Converts a string slice to a raw pointer.let pointer_num string.as_ptr() as usize;let length string.len();let res get_str(pointer_num, length);println!(The {} bytes at 0x{:X} stored: {},length, pointer_num, res)
}我们将unsafe的部分单独抽成了一个函数。这里的返回值不想用String交出所有权也可以用static 的str
或者更简单地可以直接将res右侧全部用unsafe包裹
let res unsafe{ from_utf8_unchecked(from_raw_parts(pointer_num as *const u8, length));}3. FFI
FFIForeign Function Interface可以用来与其它语言进行交互将 C/C 的代码重构为 Rust 时先将相关代码引入到 Rust 项目中然后逐步重构也是不错的。
当然除了 FFI 还有一个办法可以解决跨语言调用的问题那就是将其作为一个独立的服务然后使用网络调用的方式去访问HTTPgRPC 都可以。
言归正传之前我们提到 unsafe 的另一个重要目的就是对 FFI 提供支持它的全称是 Foreign Function Interface顾名思义通过 FFI , 我们的 Rust 代码可以跟其它语言的外部代码进行交互。
在Rust中调用其他语言的函数
下面的例子演示了如何调用 C 标准库中的 abs 函数Rust 目前无法直接调用 C 库
extern C {fn abs(input: i32) - i32;
}fn main() {unsafe {println!(Absolute value of -3 according to C: {}, abs(-3));}
}事实上不指定 ABI 字符串的默认情况下外部块会假定使用指定平台上的标准 C ABI 约定来调用当前的库。所以上面的代码这么写也是ok的
extern {fn abs(input: i32) - i32;
}当然大括号不能去掉。在 extern “C” 代码块中我们列出了想要调用的外部函数的签名。其中 “C” 定义了外部函数所使用的应用二进制接口ABI (Application Binary Interface)ABI 定义了如何在汇编层面来调用该函数。
有三个 ABI 字符串是跨平台的并且保证所有编译器都支持它们
extern Rust – 在任何 Rust 语言中编写的普通函数 fn foo() 默认使用的 ABI。extern C – 这等价于 extern fn foo()无论您的 C编译器支持什么默认 ABI。extern system – 在 Win32 平台之外中通常等价于 extern C。在 Win32 平台上应该使用stdcall或者其他应该使用的 ABI 字符串来链接它们自身的 Windows API。
4. 访问或修改一个可变的静态变量
静态变量
静态变量允许声明一个全局的变量常用于全局数据统计例如我们希望用一个变量来统计程序当前的总请求数
static mut REQUEST_RECV: usize 0;
fn main() {unsafe {REQUEST_RECV 1;assert_eq!(REQUEST_RECV, 1);}
}Rust 要求必须使用unsafe语句块才能访问和修改static变量因为这种使用方式往往并不安全其实编译器是对的当在多线程中同时去修改时会不可避免的遇到脏数据。
只有在同一线程内或者不在乎数据的准确性时才应该使用全局静态变量。
和常量相同定义静态变量的时候必须赋值为在编译期就可以计算出的值(常量表达式/数学表达式)不能是运行时才能计算出的值(如函数)
5. 实现 unsafe 特征
unsafe特征的意义是特征中存在unsafe的方法有时候就得需要unsafe的特征
unsafe trait Foo {// 方法列表
}unsafe impl Foo for i32 {// 实现相应的方法
}fn main() {}但是在调用 unsafe trait 时直接直接调用不需要在 unsafe 块中调用因为这里的安全已经被实现者保证了毕竟如果实现者没保证调用者也做不了什么来保证安全.
Rust 中的 Send / Sync 这两个 trait 都是 unsafe trait定义如下
pub unsafe auto trait Send {}
pub unsafe auto trait Sync {}6. 访问 union 中的字段
访问
这个从C中继承而来的数据结构在Rust中也大多用于和C进行交互下面就是一个union的例子
union MyUnion {f1: u32,f2: f32,
}union的关键属性是其所有字段共享公共存储。 因此对union的一个字段的写入可以覆盖其他字段并且 union的大小由其最大字段的大小决定。
fn main() {//初始化一个union语法和struct类似let u MyUnion { f1: 1 };//读取union的值let f unsafe { u.f1 };println!(u.f1 {f});
}读取值的操作是unsafe的这也很好理解编译器并不知道你读取的东西有没有初始化。反正大家都用相同的内存我说这段数据就是f32也行就算它存进去的时候其实是u32。 let f unsafe { u.f1 };let tmp unsafe { u.f2 };println!(u.f1 {f});println!(u.f2 {tmp});结果如下 也可以用模式匹配当然这种操作和直接读取没什么区别所以也必须是unsafe的 unsafe {match u {MyUnion { f1: 1 } {println!(one);}MyUnion { f2 } {println!({}, f2);}}}引用
引用操作也是unsafe的而且由于union各个成员是共享内存的对一名成员的引用会视为对其他所有成员的引用
// 错误: 不能同时对 u (通过 u.f2)拥有多于一次的可变借用
fn test() {let mut u MyUnion { f1: 1 };unsafe {let b1 mut u.f1;
// ---- 首次可变借用发生在这里 (通过 u.f1)let b2 mut u.f2;
// ^^^^ 二次可变借用发生在这里 (通过 u.f2)*b1 5;}
// - 首次借用在这里结束assert_eq!(unsafe { u.f1 }, 5);
}Rust-Analysis也给出了提示
C的改进
union存在很多问题因此C17设计了一个新的variant替代原来的union。 variant的用法如下
using namespace std;int main()
{variantint, string, float myVar;myVar Hello variant;
}union访问的时候由于每个成员变量都有自己的变量名因此直接就可以访问。但是variant不太行而且还要更麻烦一点。 最简单的就是用get
cout getstring(myVar) endl;但是这里存在一个问题如果类型对了那皆大欢喜类型错了还要处理抛出的std::bad_variant_access异常
我们可以使用get_if先判断类型再进行访问。get_if判断类型成功会返回指向数据的指针判断失败会返回空指针。
if(auto ptr get_ifstring(myVar))
{cout *ptr endl;
}7. 内联汇编
Rust中的内联汇编
Rust 提供了 asm! 宏可以让大家在 Rust 代码中嵌入汇编代码对于一些极致高性能或者底层的场景还是非常有用的例如操作系统内核开发。
use std::arch::asm;unsafe {asm!(nop);
}上面代码将插入一个 NOP 指令( 空操作 ) 到编译器生成的汇编代码中其中指令作为 asm! 的第一个参数传入。
总结
C中其实没有unsafe这个东西像类似裸指针这种在C中甚至是一种比较常用的用法。毕竟智能指针比如shared_ptr,unique_ptr用法更为复杂。
所以我个人认为Rust的unsafe的意义是将这些不安全的操作变得复杂变得难写进而引导程序员选择更加简单更加好写的安全用法。这和C如今的处境刚好相反C中按照安全原则写出来的代码都比较复杂这也是历史原因毕竟不能动现成的代码。
另外unsafe也是一种承诺不再由编译器保证代码的安全性而是由程序员自己来保证。一旦代码出问题责任全在程序员自己。