pointersrustreferencedereferencerefcell

How to understand the associativity of `&*` in a chained call composed of `as_ref()` and `as_ptr()`?


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:

  1. Is & 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())))?
  2. &* 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")?
  3. Related to 2. In *(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 &?

Solution

    1. Yes, & and * have the same precedence, are among the lowest precedence unary operators. See Rust Reference: Expression precedence

    2. 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.

    1. Let's split up that first line even more:
    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.