无需unsafe代码即可使用原始指针的实用场景探究
哈哈,这个问题问得太到位了!很多人一提到原始指针(raw pointer)就直接联想到unsafe,但其实Rust里还真有不少不用写一行unsafe就能合法使用它们的实用场景,我给你捋几个最常见的:
无需
unsafe的原始指针实用场景 1. 作为类型标记/逻辑关联的占位符
有时候我们需要在自定义类型里标记“这个类型逻辑上关联了某类资源的指针”,但又不需要实际持有或操作资源内容。这时候原始指针可以用来当一个轻量的标记,完全不用unsafe:
// 用空类型指针标记这是一个文件资源句柄 struct FileHandle(*const ()); impl FileHandle { // 安全创建句柄,用空指针作为初始标记 fn new() -> Self { FileHandle(std::ptr::null()) } }
这里的*const ()只是个类型标记,用来区分不同的资源句柄类型,全程不需要触碰unsafe。
2. 空指针的判断与传递
Rust提供了std::ptr::null()、std::ptr::null_mut()这些安全函数,返回的空指针可以在安全代码里自由使用:
- 用来表示“无资源”的状态
- 作为参数传递给需要空指针的API(调用C API本身是
unsafe,但准备空指针的过程是安全的) - 用
is_null()方法安全判断指针是否为空
示例代码:
fn get_optional_resource() -> *const u8 { // 模拟根据业务逻辑返回空指针或占位指针 if resource_unavailable() { std::ptr::null() } else { // 这里的 dangling() 返回一个不指向有效内存的指针,但创建它是安全的 unsafe { std::ptr::NonNull::dangling().as_ptr() } } } // 安全判断指针是否为空 fn has_resource(ptr: *const u8) -> bool { !ptr.is_null() }
3. 安全的指针类型转换
只要不涉及解引用,原始指针的类型转换是完全安全的。比如:
- 可变指针转不可变指针:
*mut T→*const T - 任意类型指针转空类型指针:
*const T→*const () - 指针的宽化转换(比如
*const u8→*const i8)
示例:
let mut num = 42; let mut_ptr: *mut i32 = &mut num; // 安全转换:可变转不可变 const_ptr: *const i32 = mut_ptr; // 安全转换:转成通用空类型指针 unit_ptr: *const () = const_ptr as *const ();
这种转换在需要统一处理不同类型的资源指针时特别有用。
4. 作为哈希集合的键或存入容器
原始指针已经实现了Hash和Eq trait,所以可以安全地放进HashSet或者作为HashMap的键,用来追踪内存地址的唯一性,全程不用unsafe:
use std::collections::HashSet; fn track_active_resources() { let mut active = HashSet::new(); let resource_a = 100; let ptr_a = &resource_a as *const i32; active.insert(ptr_a); // 安全判断指针是否已被追踪 assert!(active.contains(&ptr_a)); }
这里只是比较指针的地址值,完全不涉及内存访问,所以是安全的。
5. 在常量中定义原始指针
Rust允许在安全的常量上下文中定义原始指针,比如指向静态变量的指针,或者空指针:
// 安全定义空指针常量 const NULL_U32_PTR: *const u32 = std::ptr::null(); // 指向静态变量的指针,编译时就能确定地址,安全定义 static GLOBAL_CONFIG: i32 = 99; const CONFIG_PTR: *const i32 = &GLOBAL_CONFIG;
核心逻辑总结
这些场景的共同点是:只操作指针本身的地址或类型,绝不进行解引用、读写指针指向的内存这类会触发内存不安全的操作。Rust的安全机制允许我们在安全代码里持有和操作原始指针,只要不越界触碰那些需要unsafe才能做的事。
内容的提问来源于stack exchange,提问作者at54321
相关产品推荐
相关产品推荐

