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
,并且始终将其封装在安全的抽象中。