Taking ownership via shared reference yet still valid

I'm reading how to write a linked list in rust in this website Introduction - Learning Rust With Entirely Too Many Linked Lists. In the unsafe implementation of the linked list section, there is the following (valid) code.

use std::ptr;

pub struct List<T> {
    head: *mut Node<T>,
    tail: *mut Node<T>,
}

struct Node<T> {
    elem: T,
    next: *mut Node<T>,
}

pub struct Iter<'a, T> {
    next: Option<&'a Node<T>>,
}

pub struct IterMut<'a, T> {
    next: Option<&'a mut Node<T>>,
}

impl<T> List<T> {
    pub fn new() -> Self {
        List { head: ptr::null_mut(), tail: ptr::null_mut() }
    }

    pub fn iter(&self) -> Iter<'_, T> {
        unsafe {
            Iter { next: self.head.as_ref() }
        }
    }

    pub fn iter_mut(&mut self) -> IterMut<'_, T> {
        unsafe {
            IterMut { next: self.head.as_mut() }
        }
    }
}

But the signature of function as_ref or as_mut is: (take as_ref in fn iter as an example)

pub const unsafe fn as_ref<'a>(self) -> Option<&'a T>

So how on earth can we take the ownership of self.head via &Self, a shared borrow? Is it because of unsafe? However within the range of the five extra tools that unsafe can provide us with, I don't think literally there is such a thing as taking ownership of a thing via shared reference. I'm also confused with how to create a 'a out of nowhere in as_ref, but I don't think that's my priority now because my mind's just a mess now with the link list in Rust and the unsafe part.

In the context of List::<T>::iter, that's

[..] fn as_ref<'a>(self: *mut Node<T>) -> Option<&'a Node<T>>

Raw pointers like *mut _ implement Copy, and this particular case is more like a &self or &mut self parameter as far as ownership goes. The ownership of *self is not being transferred.

But in other cases, passing a raw pointer may indeed transfer ownership. As with C/C++ pointers, there's no inherent ownership or lack of ownership; it comes down to API documentation.

It can take on any lifetime; it's up to the caller to make sure the lifetime it takes on makes sense somehow. That's part of why the function is unsafe. In the example it ends up taking on the same "value" as the lifetime of &self passed to iter due to the function signature.

2 Likes

I didn't realize it, it's like a trap in my mind that I couldn't get out of by myself. Thanks!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.