I'm reading linkedlist
, an example of how to implement Iterator
for a linked list from Brenden Matthews's Book Idiomatic Rust (Chapter 6). The complete code can be accessed from Source code the same website (ch06/linkedlist/src/lib.rs
). Below are excerpted snippets to show my confusion.
type ItemData<T> = Rc<RefCell<T>>;
type ListItemPtr<T> = Rc<RefCell<ListItem<T>>>;
struct ListItem<T> {
data: ItemData<T>,
next: Option<ListItemPtr<T>>,
}
pub struct LinkedList<T> {
head: Option<ListItemPtr<T>>,
}
impl<T> LinkedList<T> {
pub fn new() -> Self {
... // code omitted
}
pub fn append(&mut self, t: T) {
... // code omitted
}
pub fn iter(&self) -> Iter<T> {
Iter {
next: self.head.clone(),
data: None,
phantom: PhantomData,
}
}
}
pub struct Iter<'a, T> {
next: Option<ListItemPtr<T>>,
data: Option<ItemData<T>>,
phantom: PhantomData<&'a 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,
}
}
}
My questions are all about the last part unsafe { Some(&*self.data.as_ref().unwrap().as_ptr()) }
. They are:
&
and *
always of the lowest precedence? That is, is &*self.data.as_ref().unwrap().as_ptr())
always evaluated to &(*(self.data.as_ref().unwrap().as_ptr())))
?&*
seemingly should cancel out hence it's said that they usually indicate implicit deref()
. Does deref()
happen here? If so, where does it happen? If not, why does leaving &*
out result in a mismatched types
error ("expected reference &T
found raw pointer *mut T
")?*(self.data.as_ref().unwrap().as_ptr())
what is the purpose of converting &Rc<RefCell<T>>
(which is the type hint from rust-analyzer for self.data.as_ref().unwrap()
) to a raw pointer *const RefCell<T>
(my understanding, not rust-analyzer's as it failed to give type hint) then *
dereferencing it? What is dereferenced to before the final, leftmost &
?Yes, &
and *
have the same precedence, are among the lowest precedence unary operators. See Rust Reference: Expression precedence
For references, that is generally true. But in your case, the *
is a raw pointer dereference, which is why you need unsafe
. Here is that expression, split apart:
let data_ptr: *mut T = self.data.as_ref().unwrap().as_ptr();
let data_reref: &T = &( // Taking a reference to that "place"
unsafe { *data_ptr } // Dereferencing the raw pointer to get the "place"
);
Some(data_reref)
In this case, &*
is being used to convert from raw pointer to reference.
let data_ref: &Rc<RefCell<T>> = self.data.as_ref().unwrap(); // Option::as_ref, Option::unwrap
let data_ptr: *mut T = data_ref.as_ptr(); // RefCell::as_ptr
as_ptr
here resolves to RefCell::as_ptr
because Rc::as_ptr
cannot be method-called. It can only be called like Rc::as_ptr(an_rc)
because it doesn't take a self
argument. RefCell::as_ptr
gives us a raw pointer to the data held within the RefCell
.
This is necessary because a reference attained by .borrow()
ing from the RefCell
could not escape the function body and be returned.