Skip to content

pointer guide, rc and arc #16756

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from

Conversation

steveklabnik
Copy link
Member

Adding a little bit of text to the pointer guide on reference counting.

I'd like some more examples, but I'm not sure of good ones.


`Rc<T>` is named 'arr cee' after the first two letters of 'reference counting.'
`Arc<T>` (said like 'ark') provides an 'atomic reference count'ed type. The
difference is that `Arc<T>` is threadsafe, but more expensive. The types are
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably worth noting the only way in which Arc is more expensive than Rc is when actually touching the reference count, i.e. clone and when dropping (and, I suppose, when calling downgrade or upgrade). For general use/Deref, Arc is just as cheap as Rc.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@Gankra
Copy link
Contributor

Gankra commented Aug 26, 2014

Dunno if this is the sort of example you want, but you can write a nice little persistent list with Rc pretty simply (minimal adaption of my own impl elsewhere):

#![feature(unsafe_destructor)]

use std::rc::{try_unwrap, Rc};

struct Node<T> {
    elem: T,
    next: Option<Rc<Node<T>>>,
}

impl<T> Node<T> {
    #[inline]
    fn new(elem: T) -> Node<T> {
        Node { elem: elem, next: None }
    }
}

/// An immutable singly-linked list, as seen in basically every functional language
pub struct ImmutSList<T> {
    front: Option<Rc<Node<T>>>,
    length: uint,
}

impl<T> ImmutSList<T> {
    /// Constructs a new, empty `ImmutSList`
    #[inline]
    pub fn new () -> ImmutSList<T> {
        ImmutSList{ front: None, length: 0 }
    }

    /// Gets the length of the list
    #[inline]
    pub fn len(&self) -> uint {
        self.length
    }

    /// Returns a copy of the list, with `elem` appended to the front
    #[inline]
    pub fn append (&self, elem: T) -> ImmutSList<T>{
        let mut new_node = Node::new(elem);
        new_node.next = self.front.clone();

        ImmutSList{
            front: Some(Rc::new(new_node)),
            length: self.len() + 1,
        }
    }

    /// Returns a reference to the first element in the list
    #[inline]
    pub fn head (&self) -> Option<&T> {
        self.front.as_ref().map(|node| &node.elem)
    }

    /// Returns a copy of the list, with the first element removed
    #[inline]
    pub fn tail (&self) -> ImmutSList<T> {
        let len = self.len();
        if len <= 1 {
            ImmutSList::new()
        } else {
            ImmutSList {
                length: len - 1,
                front: self.front.as_ref().unwrap().next.clone(),
            }
        }
    }
}

#[unsafe_destructor]
impl<T> Drop for ImmutSList<T> {
    fn drop (&mut self) {
        // don't want to blow the stack with destructors,
        // but also don't want to walk the whole list.
        // So walk the list until we find a non-uniquely owned item
        let mut head = self.front.take();
        loop {
            let temp = head;
            match temp {
                Some(node) => match try_unwrap(node) {
                    Ok(mut node) => {
                        head = node.next.take();
                    }
                    _ => return,
                },
                _ => return,
            }
        }
    }
}

@Gankra
Copy link
Contributor

Gankra commented Aug 26, 2014

The rc module itself also features some ok examples.

@reem
Copy link
Contributor

reem commented Aug 26, 2014

A persistent list is a great example, especially because you can easily create a concurrent abstraction with Arc, I have a version here: https://github.com/reem/adamantium/blob/master/src/list.rs

@Gankra
Copy link
Contributor

Gankra commented Aug 26, 2014

@reem: Your version will blow the stack with recursive destructors if it becomes too large, right? (Not sure if Arc's are more magic than that)

@reem
Copy link
Contributor

reem commented Aug 28, 2014

@gankro probably. I haven't had much time to work on making adamantium a really good library yet. How would you suggest I fix that?

@Gankra
Copy link
Contributor

Gankra commented Aug 28, 2014

@reem Check out the unsafe destructor in the sample I posted. Basically (assuming I wrote it write), you have to manually walk the nodes and claim ownership of them one at a time, so that when the parent node gets destroyed, it doesn't trigger the destructor of the child. Thankfully, you only have to walk to the next node while the reference you hold is unique, because any other kind of [A]rc won't trigger a recursive destructor on the child. Thus, even though you potentially walk the whole list every time, it's only amortized O(1) in the number of persistent appends you perform.

@steveklabnik
Copy link
Member Author

Was doing more work on this, and ran into #16821, so this is on hold until that gets fixed.

I am actually not happy with this draft anyway, so I'm just going to close this. Thanks though!

matthiaskrgr pushed a commit to matthiaskrgr/rust that referenced this pull request Mar 10, 2024
internal: Adjust a few things for trait assoc item hovers

rust-lang/rust-analyzer#15938 (minor turned major wrt diff because of test changes)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants