为何Rust中UnsafeCell两种可变引用写法编译结果不同?
为什么
&mut unsafe { *cell.get() }返回时编译失败,而unsafe { &mut *cell.get() }可以? 这本质是Rust对临时值的处理规则、引用与原始数据绑定关系的差异,并非“对同一对象的借用”,具体拆解如下:
1. unsafe { &mut *cell.get() }的工作逻辑
cell.get()返回指向UnsafeCell内部值的裸指针*mut i32。- 在unsafe块中,
&mut *是将裸指针直接转换为可变引用,这个引用直接绑定到UnsafeCell持有的原始数据,其生命周期可以和输入参数&UnsafeCell<i32>保持一致(只要cell存在,内部数据就存在)。 - 编译器能推断出该引用的生命周期与函数参数
cell相同,因此可以安全作为返回值返回。
2. &mut unsafe { *cell.get() }编译失败的核心原因
unsafe { *cell.get() }是对裸指针解引用,由于i32是Copy类型,这一步会生成原始值的临时拷贝,而非直接引用原始数据。- 后续的
&mut是对这个临时拷贝取可变引用,但临时值的生命周期仅局限于当前表达式(函数返回时,临时值会被立即销毁),返回该引用会导致悬垂引用,因此编译器直接报错“无法返回对临时值的引用”。
3. 为什么main函数中&mut unsafe { *cell.get() }能编译?
在main函数中,Rust会自动延长临时值的生命周期,使其与持有该引用的变量(ref_compiles_fine_2)生命周期一致。但要注意:这个引用指向的是临时副本,而非UnsafeCell内部的原始数据。你可以通过修改值验证这一点:
fn main(){ let cell = UnsafeCell::new(1); let ref_compiles_fine = unsafe { &mut *cell.get() }; *ref_compiles_fine = 2; println!("原始cell值:{}", unsafe { *cell.get() }); // 输出2,修改的是原始数据 let ref_compiles_fine_2 = &mut unsafe { *cell.get() }; *ref_compiles_fine_2 = 3; println!("原始cell值:{}", unsafe { *cell.get() }); // 仍输出2,修改的是临时副本 }
总结
两种写法的本质完全不同:
unsafe { &mut *cell.get() }操作的是UnsafeCell内部的原始数据,引用生命周期绑定到cell本身;&mut unsafe { *cell.get() }操作的是原始数据的临时拷贝,引用指向的是即将销毁的临时对象(函数返回场景)或独立的副本(main场景)。
内容的提问来源于stack exchange,提问作者Some Name
相关产品推荐
相关产品推荐

