Skip to content

Enabling Rc<Trait> #981

Closed
Closed
@mahkoh

Description

@mahkoh

Given some additional intrinsics and extended subtyping support in function signatures, it's possible to write Rc<Trait>:

Extended subtyping support means that we can always write <X, Y: X> which means that Y is a subtype of X. Right now this syntax only works if X is a trait, but it should also work if X == Y. See the implementation below.

/// `Rc<T>` for sized and unsized `T`
///
/// How to use it:
///
/// Given two types `T`, `U` such that `T` is a subtype of `U`, and `x: T`, the following
/// just works:
///
/// ```
/// let u: Rc<U> = Rc::new(x);
/// let u_ref: &U = &*u;
/// ```
///
/// Implementation:
///
/// We use the following new intrinsics in the implementation:
///
/// ```
/// fn deref_drop<T>(ptr: *const T);
/// fn deref_size<T>(ptr: *const T) -> usize;
/// fn deref_align<T>(ptr: *const T) -> usize;
/// fn deref_addr<T>(ptr: *const T) -> *const ();
/// ```
///
/// For `T` sized this is merely what already exists in `std::intrinsics`. For `T` a
/// trait:
///
/// - `deref_drop` - Deref drop drops the object contained in the trait object, e.g.,
///   `deref_drop(&String::new() as &Any)` drops the string.
/// - `deref_size` - Returns the size of the object contained in the trait object, e.g.,
///   `deref_drop(&String::new() as &Any) == size_of::<String>`.
/// - `deref_size` - Returns the alignment of the object contained in the trait object.
/// - `deref_addr` - Returns the address of the object contained in the trait object.
///
/// Similar for `T` a slice.

use intrinsics::{deref_drop, deref_size, deref_align, deref_addr};

/// Data shared between all `Rc` and `Weak`.
#[repr(C)]
struct Shared<T> {
    strong: Cell<usize>,
    weak:   Cell<usize>
    value:  T,
}

impl<T> Shared<T> {
    fn bump_weak(&self) -> usize {
        let weak = self.weak.get() + 1;
        self.weak.set(weak);
        weak
    }

    fn dec_weak(&self) -> usize {
        let weak = self.weak.get() - 1;
        self.weak.set(weak);
        weak
    }

    fn bump_strong(&self) -> usize {
        let strong = self.strong.get() + 1;
        self.strong.set(strong);
        strong
    }

    fn dec_strong(&self) -> usize {
        let strong = self.strong.get() - 1;
        self.strong.set(strong);
        strong
    }
}

pub struct Rc<T: ?Sized> {
    _ref: *const T,
}

impl<T: ?Sized> !marker::Send for Rc<T> {}
impl<T: ?Sized> !marker::Sync for Rc<T> {}

impl<T: ?Sized> Rc<T> {
    pub fn new<X: T>(value: X) -> Rc<T> {
        unsafe {
            let shared = box Shared {
                strong: Cell::new(1),
                weak:   Cell::new(1)
                value:  value,
            };
            let _ref = mem::transmute::<&T, *const T>(&shared.value as &T);
            mem::forget(shared);
            Rc { _ref = _ref }
        }
    }

    pub fn downgrade(&self) -> Weak<T> {
        let shared = self.shared();
        shared.bump_weak();
        Weak { _ref = self._ref }
    }

    fn shared(&self) -> &Shared<()> {
        unsafe {
            let addr = (deref_addr(self._ref) as *const usize).offset(-2);
            mem::transmute(addr)
        }
    }
}

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

    fn deref(&self) -> &T {
        unsafe { mem::transmute(self._ref) }
    }
}

impl<T: ?Sized> Drop for Rc<T> {
    fn drop(&mut self) {
        unsafe {
            let shared = self.shared();
            if shared.dec_strong() > 0 {
                return;
            }
            // This was the last strong reference
            deref_drop(self._ref);
            if shared.dec_weak() > 0 {
                return;
            }
            // There are no more strong or weak references
            let size = 2*size_of::<Cell<usize>> + deref_size(self._ref);
            let align = cmp::max(deref_align(self._ref), min_align_of(Cell<usize>>));
            deallocate(shared as *const _ as *mut u8, size, align);
        }
    }
}

impl<T: ?Sized> Clone for Rc<T> {
    fn clone(&self) -> Rc<T> {
        let shared = self.shared();
        shared.inc_strong();
        Rc { _ref: self._ref }
    }
}

pub struct Weak<T: ?Sized> {
    _ref: *const T,
}

impl<T: ?Sized> !marker::Send for Weak<T> {}
impl<T: ?Sized> !marker::Sync for Weak<T> {}

impl<T: ?Sized> Weak<T> {
    pub fn upgrade(&self) -> Option<Rc<T>> {
        let shared = self.shared();
        if shared.inc_strong() > 1 {
            Some(Rc { _ref: self._ref })
        } else {
            shared.dec_strong();
            None
        }
    }

    fn shared(&self) -> &Shared<()> {
        unsafe {
            let addr = (deref_addr(self._ref) as *const usize).offset(-2);
            mem::transmute(addr)
        }
    }
}

impl<T: ?Sized> Drop for Weak<T> {
    fn drop(&mut self) {
        unsafe {
            let shared = self.shared();
            if shared.dec_weak() == 0 {
                let size = 2*size_of::<Cell<usize>> + deref_size(self._ref);
                let align = cmp::max(deref_align(self._ref), min_align_of(Cell<usize>>));
                deallocate(shared as *const _ as *mut u8, size, align);
            }
        }
    }
}

impl<T> Clone for Weak<T> {
    fn clone(&self) -> Weak<T> {
        let shared = self.shared();
        shared.inc_weak();
        Weak { _ref: self._ref }
    }
}

cc @nikomatsakis

Metadata

Metadata

Assignees

No one assigned

    Labels

    T-langRelevant to the language team, which will review and decide on the RFC.T-libs-apiRelevant to the library API team, which will review and decide on the RFC.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions