Too-many-lists: Explain `&**node`

Created on 10 Aug 2015  ·  10Comments  ·  Source: rust-unofficial/too-many-lists

From https://www.reddit.com/r/rust/comments/3geb5q/learning_rust_with_entirely_too_many_linked_lists/ctxgwmz, maybe explain &**node to make it more obvious what is being dereferenced. Coming from C, this was a little confusing.

Most helpful comment

I tried @m-ou-se 's suggestion in playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7156f8fdd1d3c883379b5de868f230be

Curious, I went to read the source code of Box::deref, and voila, there it is:

impl<T: ?Sized> Deref for Box<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &**self
    }
}

I believe this can be introduced in the same vein as the pattern of take() and map().

Btw, the more general Deref::deref also works.

All 10 comments

Thanks for logging the issue :+1:

Just stumbled upon myself on this, I didn't even caught the new ** in 3.5 iter, at line 369 (or after “ (ノಥ益ಥ)ノ ┻━┻ ”) and was very surprised to see it in the final code.

I also didn't fully understood was it was so obvious that we needed as_ref(). Expanding a little on that would do no harm. :)

I was looking through this section, should the &**node on line 259 be &*node? I may have missed something, but I didn't see a reason for it to change from the previous section.

https://github.com/Gankro/too-many-lists/blame/master/text/second-iter.md#L259

Similar issue in the error at line 123123.

https://github.com/Gankro/too-many-lists/blame/master/text/second-iter.md#L123

Remember what we want at Iter::next is of type Option<&Node<T>>, and & is reference while * is dereference just as in C.

self.head is of type Option<Box<Node<T>>> so without as_ref(), inside map() node is of type Box<Node<T>>. One dereference, *, to get rid of Box<> and take out Node<T>. We then refer to it with &.

With as_ref() returning Option<&Box<Node<T>>>, though, inside map() node becomes type &Box<Node<T>>. One dereference for & and another for Box<>, two dereferences are needed to take out Node<T>. It is then referred to with &.

@mackwic
IMHO, the theme is we cannot simply move the result out of an Option<>. Either we take it with take() or map(), or view it with as_ref(). If we take it with map() but only returns a reference, the node itself goes out of scope as soon as the reference is returned. So we have to first view it as_ref(), then map() the reference out to dereference the node.

In keeping with the theme of the book, it probably would be good for the section to have another case of the compiler screaming at us for the mismatched type after adding as_ref and then fixing it and explaining why. Also, there are a few parts where the text gives a spoiler and shows ** before that change was necessary.

I'm stil working through the book and always try to implement the current feature (e.g. .iter()) on my own (using only the Rust documentation) before looking at the "official" solution. My version of iter() ended up looking a little bit different:

pub struct List<T> {
    head: Link<T>,
}

type Link<T> = Option<Box<Node<T>>>;

struct Node<T> {
    elem: T,
    next: Link<T>,
}

impl<T> List<T> {
    // [...]

    pub fn iter(&self) -> Iter<T> {
        Iter(&self.head)
    }
}

pub struct Iter<'a, T: 'a>(&'a Link<T>);

impl<'a, T> Iterator for Iter<'a, T> {
    type Item = &'a T;

    fn next(&mut self) -> Option<Self::Item> {
        self.0.as_ref().map(|node| {
            self.0 = &node.next;
            &node.elem
        })
    }
}

Not sure if this is any better or worse, but it gets rid of both the &** as well as the .map() call in List::iter(), which felt like a duplication of the code in Iter::next() to me.

Edit: Unfortunately I'm struggling to use the same approach for iter_mut() :(

It might be clearer to use .map(Box::deref) instead of .map(|node| &**node).

I just published a major update and fixing this was one of the changes.

(using Box::deref is interesting though)

I tried @m-ou-se 's suggestion in playground: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=7156f8fdd1d3c883379b5de868f230be

Curious, I went to read the source code of Box::deref, and voila, there it is:

impl<T: ?Sized> Deref for Box<T> {
    type Target = T;

    fn deref(&self) -> &T {
        &**self
    }
}

I believe this can be introduced in the same vein as the pattern of take() and map().

Btw, the more general Deref::deref also works.

Hi, I was following the tutorial today and stumbled upon this chapter.. and my solution was the following:

Iter { next: self.head.as_ref().map(|boxed_node| boxed_node.as_ref()) }

I don't know whether people usually deref & manually via * but adding another * for that purpose seemed somewhat unnatural, and above all, the resulting code doesn't look very intelligible to me.

Actually I also just went ahead to read the source code of Box.as_ref() and voila,

#[stable(since = "1.5.0", feature = "smart_ptr_as_ref")]
impl<T: ?Sized> AsRef<T> for Box<T> {
    fn as_ref(&self) -> &T {
        &**self
    }
}

I think either of Box::deref and Box.as_ref should be preferred to the current solution in the tutorial

Was this page helpful?
0 / 5 - 0 ratings

Related issues

louy2 picture louy2  ·  5Comments

Gankra picture Gankra  ·  3Comments

kontrafiktion picture kontrafiktion  ·  9Comments

JustAPerson picture JustAPerson  ·  3Comments

SimonSapin picture SimonSapin  ·  3Comments