Struct UniquePointer

Source
pub struct UniquePointer<T: UniquePointee> { /* private fields */ }
Expand description

experimental data structure that makes extensive use of unsafe rust to provide a shared pointer throughout the runtime of a rust program as transparently as possible.

UniquePointer’s design’s purpose is two-fold:

  • Leverage the implementation of circular data structures such as Lisp cons cells.

  • Making easier the task of practicing the implementation of basic computer science data-structures (e.g.: Binary Trees, Linked Lists etc) such that the concept of pointer is as close to C as possible in terms of developer experience and so when a CS teacher speaks in terms of pointers, students can use UniquePointer in their data-structures knowing that cloning their data-structures also means cloning the pointers transparently.

In fact, the author designed UniquePointer while studying the MIT CourseWare material of professor Erik Demaine in addition to studying lisp “cons” cells.

To this point the author reiterates: UniquePointer is an experimental data-structure designed primarily as a building-block of other data-structures in rust.

UniquePointer provides the methods UniquePointer::cast_mut and UniquePointer::cast_const not unlike those of raw pointers, and also implements the methods UniquePointer::as_ref and UniquePointer::as_mut with a signature compatible with that of the AsRef and AsMut traits such that users of raw pointers can migrate to UniquePointer without much difficulty.

UniquePointer is designed a way such that Enums and Structs using UniquePointer can safely clone UniquePointer while the memory address and provenance of its value is shared.

UniquePointer is able to extend lifetimes because it maintains its own reference counting outside of the rust compiler.

Reference Counting is provided by RefCounter which uses unsafe rust to ensure that ref counts are shared across cloned objects memory.

Both UniquePointer and RefCounter use relatively obscure rust techniques under the hood to allow writing in non-mut references in strategic occasions such as incrementing its reference count within its Clone implementation.

UniquePointer only supports Sized types, that is, Zero-Sized Types (ZSTs) are not supported.

Example

use unique_pointer::UniquePointer;

fn create_unique_pointer<'a>() -> UniquePointer<&'a str> {
    UniquePointer::from("string")
}
let mut value: UniquePointer<&'_ str> = create_unique_pointer();

assert_eq!(value.is_null(), false);

assert_eq!(value.is_allocated(), true);
assert!(value.addr() > 0, "address should not be null");
assert_eq!(value.is_written(), true);
assert_eq!(value.inner_ref(), &"string");

assert_eq!(value.read(), "string");
assert_eq!(value.as_ref(), Some(&"string"));

§Caveats of UniquePointer:

  • Only supports types that implement Debug
  • Does not support ZSTs (Zero-Sized Types)
  • UniquePointer IS NOT THREAD SAFE

Implementations§

Source§

impl<'c, T: UniquePointee + 'c> UniquePointer<T>

Source

pub fn null() -> UniquePointer<T>

creates a NULL UniquePointer ready to be written via write.

Source

pub fn from_ref(src: &T) -> UniquePointer<T>

creates a new UniquePointer by effectively reading the value referenced by [src]

Source

pub fn from_ref_mut(src: &mut T) -> UniquePointer<T>

from_ref_mut creates a new UniquePointer by effectively reading the value referenced by src

Source

pub unsafe fn propagate(&self) -> UniquePointer<T>

produces a copy of a UniquePointer which is not a copy in the sense that UniquePointer::is_copy returns true.

Because of that rationale a double-free occurs if there are two or more “containers” (e.g.: [struct]s and [enum]s) implementing Drop and holding the same propagated UniquePointer instance. For this reason UniquePointer::propagate is unsafe.

UniquePointer::propagate can be relatively observed as a drop-in replacement to UniquePointer::clone for cases when, for instance, swapping UniquePointer “instances” between instances of UniquePointer-containing (structs, enums and/or unions) is desired.

Example

use unique_pointer::UniquePointer;
use std::fmt::Debug;
use std::cmp::PartialEq;

#[derive(Clone, Debug)]
pub struct BinaryTreeNode<T: Debug> {
    pub item: T,
    pub parent: UniquePointer<BinaryTreeNode<T>>,
    pub left: UniquePointer<BinaryTreeNode<T>>,
    pub right: UniquePointer<BinaryTreeNode<T>>,
}
impl<T: Debug> BinaryTreeNode<T> {
    pub fn new(item: T) -> BinaryTreeNode<T> {
        BinaryTreeNode {
            item,
            parent: UniquePointer::null(),
            left: UniquePointer::null(),
            right: UniquePointer::null(),
        }
    }

    pub fn rotate_left(&mut self) {
        if self.parent.is_null() {
            if self.right.is_not_null() {
                self.parent = unsafe { self.right.propagate() };
                self.right = UniquePointer::null();
            }
        }
    }

    pub fn set_parent(&mut self, parent: &mut BinaryTreeNode<T>) {
        self.parent = UniquePointer::read_only(parent);
    }

    pub fn set_left(&mut self, left: &mut BinaryTreeNode<T>) {
        left.set_parent(self);
        self.left = UniquePointer::read_only(left);
    }

    pub fn set_right(&mut self, right: &mut BinaryTreeNode<T>) {
        right.set_parent(self);
        self.right = UniquePointer::read_only(right);
    }
}

let mut node_a = BinaryTreeNode::new("A");
let mut node_b = BinaryTreeNode::new("B");
let mut node_c = BinaryTreeNode::new("C");
node_a.set_left(&mut node_b);
node_a.set_right(&mut node_c);
Source

pub fn read_only(data: &T) -> UniquePointer<T>

calls [copy_from_ref] to create a read-only UniquePointer from a reference of T, useful for iterating over self-referential data structures.

Example:

Source

pub fn copy_from_ref(data: &T, refs: usize) -> UniquePointer<T>

calls [copy_from_mut_ptr] to create a read-only UniquePointer from a reference of T, useful for iterating over self-referential data structures that use RefCounter to count refs.

Note: [read_only] might be a better alternative when T is a data structure that does not use RefCounter.

Source

pub fn copy_from_mut_ptr(ptr: *mut T, refs: usize) -> UniquePointer<T>

creates a read-only UniquePointer from a reference of T, useful for iterating over self-referential data structures that use RefCounter to count refs.

Note: [read_only] might be a better alternative when T is a data structure that does not use RefCounter.

Source

pub fn addr(&self) -> usize

returns the value containing both the provenance and memory address of a pointer

Source

pub fn refs(&self) -> usize

returns the reference count of a UniquePointer

Source

pub fn is_null(&self) -> bool

returns true if the UniquePointer is NULL.

Source

pub fn is_not_null(&self) -> bool

returns true if the UniquePointer is not NULL. [is_not_null] is a idiomatic shortcut to negating a call to [is_null] such that the negation is less likely to be clearly visible.

Source

pub fn is_not_copy(&self) -> bool

returns true if the UniquePointer is not a copy. [is_not_copy] is a idiomatic shortcut to negating a call to [is_copy] such that the negation is less likely to be clearly visible.

Source

pub fn can_dealloc(&self) -> bool

returns true if the UniquePointer is not NULL and is not flagged as a copy, meaning it can be deallocated without concern for double-free.

Source

pub fn is_allocated(&self) -> bool

returns true if the UniquePointer has been allocated and therefore is no longer a NULL pointer.

Source

pub fn is_written(&self) -> bool

returns true if the UniquePointer has been written to

Source

pub fn is_copy(&self) -> bool

returns true if a UniquePointer is a “copy” of another UniquePointer in the sense that dropping or “hard-deallocating” said UniquePointer does not incur a double-free.

Source

pub fn alloc(&mut self)

allocates memory in a null UniquePointer

Source

pub fn cast_mut(&self) -> *mut T

compatibility API to a raw mut pointer’s pointer::cast_mut.

Source

pub fn cast_const(&self) -> *const T

compatibility API to a raw const pointer’s pointer::cast_const.

Source

pub fn write(&mut self, data: T)

allocates memory and writes the given value into the newly allocated area.

Source

pub fn write_ref_mut(&mut self, data: &mut T)

takes a mutable reference to a value and writes to a UniquePointer

Source

pub fn write_ref(&mut self, data: &T)

takes a read-only reference to a value and writes to a UniquePointer

Source

pub fn swap(&mut self, other: &mut Self)

swaps the memory addresses storing T with other UniquePointer

Source

pub fn read(&self) -> T

reads data from memory UniquePointer. Panics if the pointer is either null or allocated but never written to.

Source

pub fn try_read(&self) -> Option<T>

reads data from memory UniquePointer

Source

pub fn inner_ref(&self) -> &'c T

obtains a read-only reference to the value inside UniquePointer but does not increment references

Source

pub fn inner_mut(&mut self) -> &'c mut T

obtains a mutable reference to the value inside UniquePointer but does not increment references

Source

pub fn as_ref(&self) -> Option<&'c T>

compatibility layer to [std::pointer::as_ref]

Source

pub fn as_mut(&mut self) -> Option<&'c mut T>

compatibility layer to [std::pointer::as_mut]

Source

pub fn dealloc(&mut self, soft: bool)

deallocates a UniquePointer.

The [soft] boolean argument indicates whether the UniquePointer should have its reference count decremented or deallocated immediately.

During “soft” deallocation (soft=true) calls to dealloc only really deallocate memory when the reference gets down to zero, until then each dealloc(true) call simply decrements the reference count.

Conversely during “hard” deallocation (soft=false) the UniquePointer in question gets immediately deallocated, possibly incurring a double-free or causing Undefined Behavior.

Source

pub fn drop_in_place(&mut self)

deallocates the memory used by UniquePointer once its references get down to zero.

Source

pub fn extend_lifetime<'t>(&self) -> &'t T

utility method to extend the lifetime of references of data created within a function.

Example

use unique_pointer::UniquePointer;

pub struct Data<'r> {
    value: &'r String,
}
impl <'r> Data<'r> {
    pub fn new<T: std::fmt::Display>(value: T) -> Data<'r> {
        let value = value.to_string();
        Data {
            value: UniquePointer::read_only(&value).extend_lifetime()
        }
    }
}
Source

pub fn extend_lifetime_mut<'t>(&mut self) -> &'t mut T

utility method to extend the lifetime of references of data created within a function.

Example

use unique_pointer::UniquePointer;

pub struct Data<'r> {
    value: &'r mut String,
}
impl <'r> Data<'r> {
    pub fn new<T: std::fmt::Display>(value: T) -> Data<'r> {
        let value = value.to_string();
        Data {
            value: UniquePointer::read_only(&value).extend_lifetime_mut()
        }
    }
}
Source§

impl<T: UniquePointee> UniquePointer<T>

Source

pub fn provenance_of_const_ptr(ptr: *const T) -> usize

helper method that returns the address and provenance of a const pointer

Source

pub fn provenance_of_mut_ptr(ptr: *mut T) -> usize

helper method that returns the address and provenance of a mut pointer

Source

pub fn provenance_of_ref(ptr: &T) -> usize

helper method that returns the address and provenance of a reference

Source

pub fn provenance_of_mut(ptr: &mut T) -> usize

helper method that returns the address and provenance of a mutable reference

Trait Implementations§

Source§

impl<T: UniquePointee> AsMut<T> for UniquePointer<T>

Source§

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

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

impl<T: UniquePointee> AsRef<T> for UniquePointer<T>

Source§

fn as_ref(&self) -> &T

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

impl<T> Clone for UniquePointer<T>
where T: Debug + UniquePointee,

The Clone implementation of UniquePointer is special because it flags cloned values as clones such that a double-free doesn not occur.

Source§

fn clone(&self) -> UniquePointer<T>

Returns a duplicate of the value. Read more
1.0.0 · Source§

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

Performs copy-assignment from source. Read more
Source§

impl<T> Debug for UniquePointer<T>
where T: Debug + UniquePointee,

Source§

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

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

impl<T: UniquePointee> Deref for UniquePointer<T>

Source§

type Target = T

The resulting type after dereferencing.
Source§

fn deref(&self) -> &T

Dereferences the value.
Source§

impl<T: UniquePointee> DerefMut for UniquePointer<T>

Source§

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

Mutably dereferences the value.
Source§

impl<T> Drop for UniquePointer<T>
where T: Debug + UniquePointee,

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

impl<T> From<&T> for UniquePointer<T>
where T: Debug + UniquePointee,

Source§

fn from(data: &T) -> UniquePointer<T>

Converts to this type from the input type.
Source§

impl<T> From<&mut T> for UniquePointer<T>
where T: Debug + UniquePointee,

Source§

fn from(data: &mut T) -> UniquePointer<T>

Converts to this type from the input type.
Source§

impl<T> From<T> for UniquePointer<T>
where T: Debug + UniquePointee,

Source§

fn from(data: T) -> UniquePointer<T>

Converts to this type from the input type.
Source§

impl<T: UniquePointee + Hash> Hash for UniquePointer<T>

Source§

fn hash<H: Hasher>(&self, state: &mut H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl<T: UniquePointee + Ord> Ord for UniquePointer<T>

Source§

fn cmp(&self, other: &Self) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 · Source§

fn max(self, other: Self) -> Self
where Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 · Source§

fn min(self, other: Self) -> Self
where Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 · Source§

fn clamp(self, min: Self, max: Self) -> Self
where Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl<T: UniquePointee + PartialEq> PartialEq<T> for UniquePointer<T>

Source§

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

Tests for self and other values to be equal, and is used by ==.
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: UniquePointee + PartialEq> PartialEq for UniquePointer<T>

Source§

fn eq(&self, fles: &UniquePointer<T>) -> bool

Tests for self and other values to be equal, and is used by ==.
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: UniquePointee + PartialOrd> PartialOrd<T> for UniquePointer<T>

Source§

fn partial_cmp(&self, other: &T) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

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

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

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

Tests less than or equal to (for self and other) and is used by the <= operator. Read more
1.0.0 · Source§

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

Tests greater than (for self and other) and is used by the > operator. Read more
1.0.0 · Source§

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

Tests greater than or equal to (for self and other) and is used by the >= operator. Read more
Source§

impl<T: UniquePointee + PartialOrd> PartialOrd for UniquePointer<T>

Source§

fn partial_cmp(&self, other: &UniquePointer<T>) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 · Source§

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

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 · Source§

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

Tests less than or equal to (for self and other) and is used by the <= operator. Read more
1.0.0 · Source§

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

Tests greater than (for self and other) and is used by the > operator. Read more
1.0.0 · Source§

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

Tests greater than or equal to (for self and other) and is used by the >= operator. Read more
Source§

impl<T> Pointer for UniquePointer<T>
where T: Debug + UniquePointee,

Source§

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

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

impl<T: UniquePointee + Eq> Eq for UniquePointer<T>

Auto Trait Implementations§

§

impl<T> Freeze for UniquePointer<T>

§

impl<T> RefUnwindSafe for UniquePointer<T>
where T: RefUnwindSafe,

§

impl<T> !Send for UniquePointer<T>

§

impl<T> !Sync for UniquePointer<T>

§

impl<T> Unpin for UniquePointer<T>

§

impl<T> UnwindSafe for UniquePointer<T>
where T: RefUnwindSafe,

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<!> for T

Source§

fn from(t: !) -> T

Converts to this type from the input type.
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.
Source§

impl<T> UniquePointee for T
where T: Debug,