Rust 中 &i32
与 *i32
的深度解析
在 Rust 中,&i32
和 *i32
是两种完全不同的指针类型,它们在安全性、所有权和使用方式上有本质区别。以下是详细对比:
核心区别概览
指针类型
引用 &i32
裸指针 *i32
安全
编译器检查
生命周期管理
不安全
手动管理
FFI/底层操作
详细对比分析
1. &i32
- 引用(安全指针)
特性
说明
安全性
100% 安全,编译器保证有效性
所有权
借用检查器控制,不转移所有权
解引用
自动解引用(Deref coercion)
使用场景
日常 Rust 编程
可变性
&i32
(不可变)或 &mut i32
(可变)
空值
永远不为空
别名规则
严格遵循借用规则(要么多个不可变引用,要么一个可变引用)
fn safe_ref_example ( ) {
let num = 42 ;
let ref_num: & i32 = & num;
println! ( "Value: {}" , * ref_num) ;
println! ( "Value: {}" , ref_num) ;
}
2. *i32
- 裸指针(不安全指针)
特性
说明
安全性
不安全,需要 unsafe
块操作
所有权
无所有权概念,可能悬垂
解引用
必须显式解引用(在 unsafe
中)
使用场景
FFI、底层系统编程、性能优化
可变性
*const i32
(不可变)或 *mut i32
(可变)
空值
可能为空(std::ptr::null()
)
别名规则
无编译时检查,需手动保证安全
fn raw_ptr_example ( ) {
let num = 42 ;
let raw_ptr: * const i32 = & num as * const i32 ;
unsafe {
println! ( "Value: {}" , * raw_ptr) ;
}
let dangling_ptr: * const i32 ;
{
let temp = 100 ;
dangling_ptr = & temp as * const i32 ;
}
}
关键区别详解
1. 安全机制对比
操作
&i32
*i32
编译时检查
借用规则
生命周期验证
运行时风险
悬垂指针
数据竞争
2. 内存访问对比
操作
&i32
*i32
创建引用/指针
&value
&value as *const i32
解引用
自动或显式 *ref
必须 unsafe { *ptr }
空指针
不可能
std::ptr::null()
指针算术
不支持
支持(ptr.offset(1)
)
类型转换
有限制(通过 trait)
自由转换(as *mut u8
)
3. 典型使用场景
&i32
场景:
fn print_value ( v: & i32 ) {
println! ( "Value: {}" , v) ;
}
struct Processor < 'a > {
data: & 'a i32 ,
}
let sum: i32 = vec! [ 1 , 2 , 3 ] . iter ( ) . map ( | & x| x * 2 ) . sum ( ) ;
*i32
场景:
extern "C" {
fn c_function ( ptr: * mut i32 ) ;
}
unsafe fn allocate_buffer ( size: usize ) -> * mut u8 {
let layout = std:: alloc:: Layout :: array :: < u8 > ( size) . unwrap ( ) ;
std:: alloc:: alloc ( layout)
}
unsafe fn fast_sum ( ptr: * const i32 , len: usize ) -> i32 {
let mut sum = 0 ;
for i in 0 .. len {
sum += * ptr. add ( i) ;
}
sum
}
转换关系
1. 安全转换
let num = 42 ;
let ref_num: & i32 = & num;
let raw_ptr: * const i32 = ref_num as * const i32 ;
2. 不安全转换
unsafe {
let ref_back: & i32 = & * raw_ptr;
}
3. 转换注意事项
as *const
unsafe &*
as *const
FFI
Rust调用
安全世界
裸指针
引用
C语言
最佳实践指南
何时使用 &i32
:
99% 的日常 Rust 编程
函数参数传递
结构体字段借用
实现 trait 对象 (&dyn Trait
)
何时使用 *i32
:
与 C 库交互(FFI)
实现底层数据结构(如自定义分配器)
极端性能优化场景
操作系统内核开发
安全使用裸指针的模式:
struct SafePointer {
ptr: * mut i32 ,
len: usize ,
}
impl SafePointer {
pub fn new ( data: & mut [ i32 ] ) -> Self {
SafePointer {
ptr: data. as_mut_ptr ( ) ,
len: data. len ( ) ,
}
}
pub fn get ( & self , index: usize ) -> Option < & i32 > {
if index < self . len {
unsafe { Some ( & * self . ptr. add ( index) ) }
} else {
None
}
}
}
struct GuardedPtr < 'a > {
ptr: * const i32 ,
_marker: std:: marker:: PhantomData < & 'a i32 > ,
}
impl < 'a > GuardedPtr < 'a > {
pub fn new ( reference: & 'a i32 ) -> Self {
GuardedPtr {
ptr: reference as * const i32 ,
_marker: std:: marker:: PhantomData ,
}
}
pub fn get ( & self ) -> & 'a i32 {
unsafe { & * self . ptr }
}
}
性能对比
操作
&i32
*i32
创建开销
零成本抽象
零成本
解引用开销
等同直接访问
等同直接访问
安全检查开销
编译时(零运行时开销)
无检查(需手动验证)
优化潜力
编译器充分优化
同左,但需更多人工干预
常见错误示例
错误1:悬垂引用
fn dangling_ref ( ) -> & 'static i32 {
let x = 42 ;
& x
}
错误2:不安全裸指针使用
fn unsafe_ptr_demo ( ) {
let ptr: * const i32 ;
{
let value = 100 ;
ptr = & value;
}
unsafe {
println! ( "{}" , * ptr) ;
}
}
错误3:违反别名规则
fn alias_violation ( ) {
let mut data = 42 ;
let ref1 = & data;
let ref2 = & mut data;
println! ( "{}" , ref1) ;
}
总结对比表
特性
&i32
*const i32
*mut i32
安全性
安全
不安全
不安全
空值
不可能
可能
可能
可变性
不可变
不可变
可变
别名检查
严格
无
无
生命周期
编译器验证
无保证
无保证
使用场景
常规编程
FFI/只读访问
FFI/写访问
解引用
安全
需 unsafe
需 unsafe
自动转换
支持 Deref
不支持
不支持
在 Rust 开发中,优先使用引用 &i32
,只有在必要时(如 FFI 或底层系统编程)才使用裸指针 *i32
,并且始终将其封装在安全的抽象中。