Rust中`&*`在`as_ref()`/`as_ptr()`链式调用中的结合性及疑问
问题背景
我正在阅读Brenden Matthews所著《Idiomatic Rust》(第6章)中实现链表Iterator的linkedlist示例,以下是引发疑问的核心代码片段:
type ItemData<T> = Rc<RefCell<T>>; type ListItemPtr<T> = Rc<RefCell<ListItem<T>>>; // 结构体及其他方法定义省略... impl<'a, T> Iterator for Iter<'a, T> { type Item = &'a T; fn next(&mut self) -> Option<Self::Item> { match self.next.clone() { Some(ptr) => { self.next = ptr.as_ref().borrow().next.clone(); self.data = Some(ptr.as_ref().borrow().data.clone()); unsafe { Some(&*self.data.as_ref().unwrap().as_ptr()) } } None => None, } } }
我的疑问均围绕unsafe { Some(&*self.data.as_ref().unwrap().as_ptr()) }部分:
&和*是否始终优先级最低?即&*self.data.as_ref().unwrap().as_ptr()是否总是等价于&(*(self.data.as_ref().unwrap().as_ptr()))?&*看似会抵消,常被认为表示隐式deref(),此处是否发生deref()?若发生,位置在哪里?若未发生,为何去掉&*会出现类型不匹配错误(“期望引用&T,找到裸指针*mut T”)?- 关联问题2:在
*(self.data.as_ref().unwrap().as_ptr())中,将&Rc<RefCell<T>>(rust-analyzer对self.data.as_ref().unwrap()的类型提示)转换为裸指针*const RefCell<T>后再解引用的目的是什么?最终最左侧&之前解引用得到的是什么类型?
解答
问题1:运算符优先级问题
是的,&和*的优先级确实是Rust里最低的一批,比方法调用(.)的优先级低得多。所以&*self.data.as_ref().unwrap().as_ptr()完全等价于&(*(self.data.as_ref().unwrap().as_ptr()))——所有的方法调用会先执行完毕,最后才处理*解引用和&取引用操作。
问题2:&*的作用与类型匹配
这里没有发生隐式Deref,&*就是先解引用裸指针再取引用的显式操作:
self.data.as_ref().unwrap().as_ptr()返回的是*const T(因为RefCell<T>的as_ptr()方法会直接返回指向内部T的裸指针,而非指向RefCell本身);- 如果去掉
&*,你拿到的就是这个裸指针*const T,但迭代器的Item要求是&'a T,类型自然不匹配,因此会报错; &*组合的作用就是把裸指针*const T先解引用成T(这一步属于unsafe操作,需要程序员自行保证裸指针的合法性),再取引用得到&T,刚好符合迭代器Item的类型要求。
问题3:解引用裸指针的目的与类型
首先纠正一个小误解:self.data.as_ref().unwrap()的类型是&ItemData<T>(即&Rc<RefCell<T>>),调用as_ptr()时,实际是调用RefCell<T>的as_ptr()方法(因为Rc会自动Deref到内部的RefCell<T>),这个方法返回的是指向RefCell内部T的*const T,而非指向RefCell的指针。
所以*(self.data.as_ref().unwrap().as_ptr())实际解引用的是指向T的裸指针,得到的是T类型的值(这一步是unsafe的,因为直接操作裸指针绕过了Rust的借用检查);最左侧的&再把这个T取引用变成&T,也就是迭代器需要返回的类型。
这么做的核心目的是:常规情况下RefCell的borrow()方法会返回Ref<T>,它有自己的生命周期,无法和迭代器的'a生命周期绑定。因此这里用不安全的方式直接拿到内部T的引用,同时靠Iter结构体里的PhantomData<&'a T>和data字段持有Rc<RefCell<T>>来保证这个引用的合法性(防止T被提前释放)。
内容的提问来源于stack exchange,提问作者foehn

