Gc

Struct Gc 

Source
pub struct Gc<T: Trace + ?Sized + 'static> { /* private fields */ }
Expand description

A garbage-collected pointer.

This garbage-collected pointer may be used for data which is not safe to share across threads (such as a std::cell::RefCell). It can also be used for variably sized data.

§Examples

use dumpster::unsync::Gc;

let x: Gc<u8> = Gc::new(3);

println!("{}", *x); // prints '3'
                    // x is then freed automatically!

§Interaction with Drop

While collecting cycles, it’s possible for a Gc to exist that points to some deallocated object. To prevent undefined behavior, these Gcs are marked as dead during collection and rendered inaccessible. Dereferencing or cloning a Gc during the Drop implementation of a Trace type could result in the program panicking to keep the program from accessing memory after freeing it. If you’re accessing a Gc during a Drop implementation, make sure to use the fallible operations Gc::try_deref and Gc::try_clone.

Implementations§

Source§

impl<T: Trace + ?Sized> Gc<T>

Source

pub fn new(value: T) -> Gc<T>
where T: Sized,

Construct a new garbage-collected allocation, with value as its value.

§Examples
use dumpster::unsync::Gc;

let gc = Gc::new(0);
Source

pub fn new_cyclic<F: FnOnce(Gc<T>) -> T>(data_fn: F) -> Self
where T: Sized,

Construct a self-referencing Gc.

new_cyclic first allocates memory for T, then constructs a dead Gc pointing to the allocation. The dead Gc is then passed to data_fn to construct a value of T, which is stored in the allocation. Finally, new_cyclic will update the dead self-referential Gcs and rehydrate them to produce the final value.

§Panics

If data_fn panics, the panic is propagated to the caller. The allocation is cleaned up normally.

Additionally, if, when attempting to rehydrate the Gc members of F, the visitor fails to reach a Gc, this function will panic and reserve the allocation to be cleaned up later.

§Notes on safety

Incorrect implementations of data_fn may have unusual or strange results. Although dumpster guarantees that it will be safe, and will do its best to ensure correct results, it is generally unwise to allow dead Gcs to exist for long. If you implement data_fn wrong, this may cause panics later on inside of the collection process.

§Examples
use dumpster::{unsync::Gc, Trace};

#[derive(Trace)]
struct Cycle {
    this: Gc<Self>,
}

let gc = Gc::new_cyclic(|this| Cycle { this });
assert!(Gc::ptr_eq(&gc, &gc.this));
Source

pub fn try_deref(gc: &Gc<T>) -> Option<&T>

Attempt to dereference this Gc.

This function will return None if self is a “dead” Gc, which points to an already-deallocated object. This can only occur if a Gc is accessed during the Drop implementation of a Trace object.

For a version which panics instead of returning None, consider using Deref.

§Examples

For a still-living Gc, this always returns Some.

use dumpster::unsync::Gc;

let gc1 = Gc::new(0);
assert!(Gc::try_deref(&gc1).is_some());

The only way to get a Gc which fails on try_clone is by accessing a Gc during its Drop implementation.

use dumpster::{unsync::Gc, Trace};
use std::cell::OnceCell;

#[derive(Trace)]
struct Cycle(OnceCell<Gc<Self>>);

impl Drop for Cycle {
    fn drop(&mut self) {
        let maybe_ref = Gc::try_deref(self.0.get().unwrap());
        assert!(maybe_ref.is_none());
    }
}

let gc1 = Gc::new(Cycle(OnceCell::new()));
gc1.0.set(gc1.clone());
Source

pub fn try_clone(gc: &Gc<T>) -> Option<Gc<T>>

Attempt to clone this Gc.

This function will return None if self is a “dead” Gc, which points to an already-deallocated object. This can only occur if a Gc is accessed during the Drop implementation of a Trace object.

For a version which panics instead of returning None, consider using Clone.

§Examples

For a still-living Gc, this always returns Some.

use dumpster::unsync::Gc;

let gc1 = Gc::new(0);
let gc2 = Gc::try_clone(&gc1).unwrap();

The only way to get a Gc which fails on try_clone is by accessing a Gc during its Drop implementation.

use dumpster::{unsync::Gc, Trace};
use std::cell::OnceCell;

#[derive(Trace)]
struct Cycle(OnceCell<Gc<Self>>);

impl Drop for Cycle {
    fn drop(&mut self) {
        let cloned = Gc::try_clone(self.0.get().unwrap());
        assert!(cloned.is_none());
    }
}

let gc1 = Gc::new(Cycle(OnceCell::new()));
gc1.0.set(gc1.clone());
Source

pub fn as_ptr(gc: &Gc<T>) -> *const T

Provides a raw pointer to the data.

Panics if self is a “dead” Gc, which points to an already-deallocated object. This can only occur if a Gc is accessed during the Drop implementation of a Trace object.

§Examples
use dumpster::unsync::Gc;
let x = Gc::new("hello".to_owned());
let y = Gc::clone(&x);
let x_ptr = Gc::as_ptr(&x);
assert_eq!(x_ptr, Gc::as_ptr(&x));
assert_eq!(unsafe { &*x_ptr }, "hello");
Source

pub fn ptr_eq(this: &Gc<T>, other: &Gc<T>) -> bool

Determine whether two Gcs are equivalent by reference. Returns true if both this and other point to the same value, in the same style as std::ptr::eq.

§Examples
use dumpster::unsync::Gc;

let gc1 = Gc::new(0);
let gc2 = Gc::clone(&gc1); // points to same spot as `gc1`
let gc3 = Gc::new(0); // same value, but points to a different object than `gc1`

assert!(Gc::ptr_eq(&gc1, &gc2));
assert!(!Gc::ptr_eq(&gc1, &gc3));
Source

pub fn ref_count(&self) -> NonZeroUsize

Get the number of references to the value pointed to by this Gc.

This does not include internal references generated by the garbage collector.

§Panics

This function may panic if the Gc whose reference count we are loading is “dead” (i.e. generated through a Drop implementation). For further reference, take a look at Gc::is_dead.

§Examples
use dumpster::unsync::Gc;

let gc = Gc::new(());
assert_eq!(gc.ref_count().get(), 1);
let gc2 = gc.clone();
assert_eq!(gc.ref_count().get(), 2);
drop(gc);
drop(gc2);
Source

pub fn is_dead(&self) -> bool

Determine whether this is a dead Gc.

A Gc is dead if it is accessed while the value it points to has been destroyed; this only occurs if one attempts to interact with a Gc during a structure’s Drop implementation. However, this is not always guaranteed - sometime the garbage collector will leave Gcs alive in differing orders, so users should not rely on the destruction order of Gcs to determine whether it is dead.

§Examples
use dumpster::{unsync::Gc, Trace};
use std::cell::OnceCell;

#[derive(Trace)]
struct Cycle(OnceCell<Gc<Self>>);

impl Drop for Cycle {
    fn drop(&mut self) {
        assert!(self.0.get().unwrap().is_dead());
    }
}

let gc1 = Gc::new(Cycle(OnceCell::new()));
gc1.0.set(gc1.clone());
Source§

impl<T: Trace + Clone> Gc<T>

Source

pub fn make_mut(this: &mut Self) -> &mut T

Makes a mutable reference to the given Gc.

If there are other Gc pointers to the same allocation, then make_mut will clone the inner value to a new allocation to ensure unique ownership. This is also referred to as clone-on-write.

§Panics

This function may panic if the Gc whose reference count we are loading is “dead” (i.e. generated through a Drop implementation). For further reference, take a look at Gc::is_dead.

§Examples
use dumpster::unsync::Gc;

let mut data = Gc::new(5);

*Gc::make_mut(&mut data) += 1; // Won't clone anything
let mut other_data = Gc::clone(&data); // Won't clone inner data
*Gc::make_mut(&mut data) += 1; // Clones inner data
*Gc::make_mut(&mut data) += 1; // Won't clone anything
*Gc::make_mut(&mut other_data) *= 2; // Won't clone anything

// Now `data` and `other_data` point to different allocations.
assert_eq!(*data, 8);
assert_eq!(*other_data, 12);

Trait Implementations§

Source§

impl<T: Trace + ?Sized> AsRef<T> for Gc<T>

Source§

fn as_ref(&self) -> &T

Converts this type into a shared reference of the (usually inferred) input type.
Source§

impl<T: Trace + ?Sized> Borrow<T> for Gc<T>

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T: Trace + ?Sized> Clone for Gc<T>

Source§

fn clone(&self) -> Self

Create a duplicate reference to the same data pointed to by self. This does not duplicate the data.

§Panics

This function will panic if the Gc being cloned points to a deallocated object. This is only possible if said Gc is accessed during the Drop implementation of a Trace value.

For a fallible version, refer to Gc::try_clone.

§Examples
use dumpster::unsync::Gc;
use std::sync::atomic::{AtomicU8, Ordering};

let gc1 = Gc::new(AtomicU8::new(0));
let gc2 = gc1.clone();

gc1.store(1, Ordering::Relaxed);
assert_eq!(gc2.load(Ordering::Relaxed), 1);

The following example will fail, because cloning a Gc to a deallocated object is wrong.

use dumpster::{unsync::Gc, Trace};
use std::cell::OnceCell;

#[derive(Trace)]
struct Cycle(OnceCell<Gc<Self>>);

impl Drop for Cycle {
    fn drop(&mut self) {
        let _ = self.0.get().unwrap().clone();
    }
}

let gc1 = Gc::new(Cycle(OnceCell::new()));
gc1.0.set(gc1.clone());
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<T: Debug + Trace + ?Sized + 'static> Debug for Gc<T>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<T: Trace + Default> Default for Gc<T>

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl<T: Trace + ?Sized> Deref for Gc<T>

Source§

fn deref(&self) -> &Self::Target

Dereference this pointer, creating a reference to the contained value T.

§Panics

This function may panic if it is called from within the implementation of std::ops::Drop of its owning value, since returning such a reference could cause a use-after-free. It is not guaranteed to panic.

For a version which returns None instead of panicking, consider Gc::try_deref.

§Examples

The following is a correct time to dereference a Gc.

use dumpster::unsync::Gc;

let my_gc = Gc::new(0u8);
let my_ref: &u8 = &my_gc;

Dereferencing a Gc while dropping is not correct.

// This is wrong!
use dumpster::{unsync::Gc, Trace};
use std::cell::RefCell;

#[derive(Trace)]
struct Bad {
    s: String,
    cycle: RefCell<Option<Gc<Bad>>>,
}

impl Drop for Bad {
    fn drop(&mut self) {
        println!("{}", self.cycle.borrow().as_ref().unwrap().s)
    }
}

let foo = Gc::new(Bad {
    s: "foo".to_string(),
    cycle: RefCell::new(None),
});
Source§

type Target = T

The resulting type after dereferencing.
Source§

impl<T: Trace + ?Sized> Drop for Gc<T>

Source§

fn drop(&mut self)

Destroy this garbage-collected pointer.

If this is the last reference which can reach the pointed-to data, the allocation that it points to will be destroyed.

Source§

impl<T: Trace + Clone> From<&[T]> for Gc<[T]>

Source§

fn from(slice: &[T]) -> Gc<[T]>

Allocates a garbage-collected slice and fills it by cloning slice’s items.

§Example
let original: &[i32] = &[1, 2, 3];
let shared: Gc<[i32]> = Gc::from(original);
assert_eq!(&[1, 2, 3], &shared[..]);
Source§

impl<T: Trace + Clone> From<&mut [T]> for Gc<[T]>

Source§

fn from(value: &mut [T]) -> Self

Allocates a garbage-collected slice and fills it by cloning v’s items.

§Example
let mut original = [1, 2, 3];
let original: &mut [i32] = &mut original;
let shared: Gc<[i32]> = Gc::from(original);
assert_eq!(&[1, 2, 3], &shared[..]);
Source§

impl From<&mut str> for Gc<str>

Source§

fn from(v: &mut str) -> Self

Allocates a garbage-collected string slice and copies v into it.

§Example
let mut original = String::from("statue");
let original: &mut str = &mut original;
let shared: Gc<str> = Gc::from(original);
assert_eq!("statue", &shared[..]);
Source§

impl From<&str> for Gc<str>

Source§

fn from(v: &str) -> Self

Allocates a garbage-collected string slice and copies v into it.

§Example
let shared: Gc<str> = Gc::from("statue");
assert_eq!("statue", &shared[..]);
Source§

impl<T: Trace, const N: usize> From<[T; N]> for Gc<[T]>

Source§

fn from(v: [T; N]) -> Gc<[T]>

Converts a [T; N] into an Gc<[T]>.

The conversion moves the array into a newly allocated Gc.

§Example
let original: [i32; 3] = [1, 2, 3];
let shared: Gc<[i32]> = Gc::from(original);
assert_eq!(&[1, 2, 3], &shared[..]);
Source§

impl<T: Trace> From<Box<T>> for Gc<T>

Source§

fn from(src: Box<T>) -> Self

Move a boxed object to a new, garbage collected, allocation.

§Example
let original: Box<i32> = Box::new(1);
let shared: Gc<i32> = Gc::from(original);
assert_eq!(1, *shared);
Source§

impl<'a, B> From<Cow<'a, B>> for Gc<B>
where B: ToOwned + ?Sized + Trace, Gc<B>: From<&'a B> + From<B::Owned>,

Source§

fn from(cow: Cow<'a, B>) -> Gc<B>

Creates a garbage-collected pointer from a clone-on-write pointer by copying its content.

§Example
let cow: Cow<'_, str> = Cow::Borrowed("eggplant");
let shared: Gc<str> = Gc::from(cow);
assert_eq!("eggplant", &shared[..]);
Source§

impl From<Gc<str>> for Gc<[u8]>

Source§

fn from(value: Gc<str>) -> Self

Converts a garbage-collected string slice into a byte slice.

§Example
let string: Gc<str> = Gc::from("eggplant");
let bytes: Gc<[u8]> = Gc::from(string);
assert_eq!("eggplant".as_bytes(), bytes.as_ref());
Source§

impl From<String> for Gc<str>

Source§

fn from(value: String) -> Self

Allocates a garbage-collected string slice and copies v into it.

§Example
let original: String = "statue".to_owned();
let shared: Gc<str> = Gc::from(original);
assert_eq!("statue", &shared[..]);
Source§

impl<T: Trace> From<T> for Gc<T>

Source§

fn from(value: T) -> Self

Converts a generic type T into an Gc<T>

The conversion allocates on the heap and moves t from the stack into it.

§Example
let x = 5;
let rc = Gc::new(5);

assert_eq!(Gc::from(x), rc);
Source§

impl<T: Trace> From<Vec<T>> for Gc<[T]>

Source§

fn from(vec: Vec<T>) -> Self

Allocates a garbage-collected slice and moves vec’s items into it.

§Example
let unique: Vec<i32> = vec![1, 2, 3];
let shared: Gc<[i32]> = Gc::from(unique);
assert_eq!(&[1, 2, 3], &shared[..]);
Source§

impl<T> PartialEq for Gc<T>
where T: Trace + ?Sized + PartialEq,

Source§

fn eq(&self, other: &Gc<T>) -> bool

Test for equality on two Gcs.

Two Gcs are equal if their inner values are equal, even if they are stored in different allocations. Because PartialEq does not imply reflexivity, and there is no current path for trait specialization, this function does not do a “fast-path” check for reference equality. Therefore, if two Gcs point to the same allocation, the implementation of eq will still require a direct call to eq on the values.

§Panics

This function may panic if it is called from within the implementation of std::ops::Drop of its owning value, since returning such a reference could cause a use-after-free. It is not guaranteed to panic. Additionally, if this Gc is moved out of an allocation during a Drop implementation, it could later cause a panic. For further details, refer to the main documentation for Gc.

§Examples
use dumpster::unsync::Gc;

let gc = Gc::new(6);
assert!(gc == Gc::new(6));
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl<T: Trace + ?Sized> Pointer for Gc<T>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<T: Trace + ?Sized> Trace for Gc<T>

Source§

fn accept<V: Visitor>(&self, visitor: &mut V) -> Result<(), ()>

Accept a visitor to this garbage-collected value. Read more
Source§

impl<T> Eq for Gc<T>
where T: Trace + ?Sized + PartialEq,

Auto Trait Implementations§

§

impl<T> !Freeze for Gc<T>

§

impl<T> !RefUnwindSafe for Gc<T>

§

impl<T> !Send for Gc<T>

§

impl<T> !Sync for Gc<T>

§

impl<T> Unpin for Gc<T>
where T: ?Sized,

§

impl<T> !UnwindSafe for Gc<T>

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<P, T> Receiver for P
where P: Deref<Target = T> + ?Sized, T: ?Sized,

Source§

type Target = T

🔬This is a nightly-only experimental API. (arbitrary_self_types)
The target type on which the method may be called.
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.