-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Closed
Labels
T-langRelevant to the language team, which will review and decide on the RFC.Relevant 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.Relevant to the library API team, which will review and decide on the RFC.
Description
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 }
}
}
Metadata
Metadata
Assignees
Labels
T-langRelevant to the language team, which will review and decide on the RFC.Relevant 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.Relevant to the library API team, which will review and decide on the RFC.