根据Rust标准库,何时可将引用视为「失效」?
Rust可变引用的「失效」到底怎么算?
先把问题说透
你纠结的点其实是:官方文档用mem::transmute实现split_at时,说原可变引用slice在某行之后不再用就视为「失效」,但这个说法太模糊——什么时候可变引用算「失效」?要是之后还要用原引用(比如你写的foo函数最后又调用bar(slice)),这逻辑不就矛盾了?
先纠正一个误解:没有「失效」,只有「暂时锁死」和「彻底没用」
Rust官方根本没定义「失效」这个词,这是文档为了方便理解搞的简化说法。本质上,这都是Rust借用规则在起作用,核心就是同一时间,同一块内存只能有一个能干活的可变引用。
1. 官方示例里的「失效」是什么意思
看split_at_stdlib的实现逻辑:
fn split_at_stdlib<T>(slice: &mut [T], mid: usize) -> (&mut [T], &mut [T]) { let ptr = slice.as_mut_ptr(); let len = slice.len(); unsafe { let ret = ( std::mem::transmute(&mut ptr[0..mid]), std::mem::transmute(&mut ptr[mid..len]), ); // 文档说的「slice在此之后不再被使用,视为失效」 ret } }
这里的「失效」其实是说:原引用slice从生成ret到函数返回这段时间里,根本没被任何代码碰过。虽然它还存在,但因为没被使用,借用检查器就不会把它当成「活跃的可变引用」,所以和返回的两个切片不会冲突——毕竟规则管的是「活跃的」可变引用,你放着不用的引用,编译器不管。
2. 你写的foo函数为什么合法
再看这个场景:
fn bar<T>(slice: &mut [T]) { ... } fn foo<T>(slice: &mut [T]) { { let (front, back) = split_at_stdlib(slice, 5); bar(front); bar(back); } // 这里是关键:front和back的作用域结束了 bar(slice); // 原引用又能用来干活了 }
这里完全符合规则:
- 当你在内部代码块里拿到
front和back时,原引用slice就被暂时锁死了——你要是在代码块里写bar(slice),编译器直接给你报错,说你同时用了多个可变引用。 - 等代码块结束,
front和back就彻底没用了(它们的存活时间到了),原引用slice的锁就解开了,这时候再用它就完全没问题。
怎么判断可变引用能不能用?记住两个关键点
别纠结「失效」这个词,换成「能不能被访问」来判断:
- 能被访问的可变引用(活跃):当前代码里还能调用、修改它,这时候绝对不能有其他指向同一块内存的活跃可变引用。
- 不能被访问的可变引用(不活跃):要么是被派生的可变引用锁死了(比如上面的
slice在代码块里的状态),要么是已经超出了它的作用域(比如函数里的局部引用,函数返回后就彻底没用了)。
简单说:
- 看作用域:如果引用已经走出它的代码块/函数,那就是彻底失效,再也用不了了。
- 看借用关系:如果从一个可变引用变出了新的可变引用,原引用在新引用活着的时候就被锁死,不能碰;等新引用死了,原引用才解锁。
最后总结一下
官方文档的「失效」就是个简化说法,本质还是Rust的核心规则:同一时间,同一块内存只能有一个能干活的可变引用。只要你别在派生引用还活着的时候去碰原引用,就不会有问题——哪怕原引用之后还要用,只要等派生引用死了再用就行。
内容的提问来源于stack exchange,提问作者ajp
相关产品推荐
相关产品推荐

