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>
impl<'c, T: UniquePointee + 'c> UniquePointer<T>
Sourcepub fn null() -> UniquePointer<T>
pub fn null() -> UniquePointer<T>
creates a NULL UniquePointer
ready to be written via write
.
Sourcepub fn from_ref(src: &T) -> UniquePointer<T>
pub fn from_ref(src: &T) -> UniquePointer<T>
creates a new UniquePointer
by effectively
reading the value referenced by [src
]
Sourcepub fn from_ref_mut(src: &mut T) -> UniquePointer<T>
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
Sourcepub unsafe fn propagate(&self) -> UniquePointer<T>
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);
Sourcepub fn read_only(data: &T) -> UniquePointer<T>
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:
Sourcepub fn copy_from_ref(data: &T, refs: usize) -> UniquePointer<T>
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
.
Sourcepub fn copy_from_mut_ptr(ptr: *mut T, refs: usize) -> UniquePointer<T>
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
.
Sourcepub fn addr(&self) -> usize
pub fn addr(&self) -> usize
returns the value containing both the provenance and memory address of a pointer
Sourcepub fn is_not_null(&self) -> bool
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.
Sourcepub fn is_not_copy(&self) -> bool
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.
Sourcepub fn can_dealloc(&self) -> bool
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.
Sourcepub fn is_allocated(&self) -> bool
pub fn is_allocated(&self) -> bool
returns true if the UniquePointer
has been
allocated and therefore is no longer a NULL pointer.
Sourcepub fn is_written(&self) -> bool
pub fn is_written(&self) -> bool
returns true if the UniquePointer
has been written to
Sourcepub fn is_copy(&self) -> bool
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.
Sourcepub fn cast_mut(&self) -> *mut T
pub fn cast_mut(&self) -> *mut T
compatibility API to a raw mut pointer’s pointer::cast_mut
.
Sourcepub fn cast_const(&self) -> *const T
pub fn cast_const(&self) -> *const T
compatibility API to a raw const pointer’s pointer::cast_const
.
Sourcepub fn write(&mut self, data: T)
pub fn write(&mut self, data: T)
allocates memory and writes the given value into the newly allocated area.
Sourcepub fn write_ref_mut(&mut self, data: &mut T)
pub fn write_ref_mut(&mut self, data: &mut T)
takes a mutable reference to a value and
writes to a UniquePointer
Sourcepub fn write_ref(&mut self, data: &T)
pub fn write_ref(&mut self, data: &T)
takes a read-only reference to a value and
writes to a UniquePointer
Sourcepub fn swap(&mut self, other: &mut Self)
pub fn swap(&mut self, other: &mut Self)
swaps the memory addresses storing T
with other UniquePointer
Sourcepub fn read(&self) -> T
pub fn read(&self) -> T
reads data from memory UniquePointer
. Panics if
the pointer is either null or allocated but never written to.
Sourcepub fn inner_ref(&self) -> &'c T
pub fn inner_ref(&self) -> &'c T
obtains a read-only reference to the value inside
UniquePointer
but does not increment references
Sourcepub fn inner_mut(&mut self) -> &'c mut T
pub fn inner_mut(&mut self) -> &'c mut T
obtains a mutable reference to the value inside
UniquePointer
but does not increment references
Sourcepub fn dealloc(&mut self, soft: bool)
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.
Sourcepub fn drop_in_place(&mut self)
pub fn drop_in_place(&mut self)
deallocates the memory used by UniquePointer
once its references get down to zero.
Sourcepub fn extend_lifetime<'t>(&self) -> &'t T
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()
}
}
}
Sourcepub fn extend_lifetime_mut<'t>(&mut self) -> &'t mut T
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>
impl<T: UniquePointee> UniquePointer<T>
Sourcepub fn provenance_of_const_ptr(ptr: *const T) -> usize
pub fn provenance_of_const_ptr(ptr: *const T) -> usize
helper method that returns the address and provenance of a const pointer
Sourcepub fn provenance_of_mut_ptr(ptr: *mut T) -> usize
pub fn provenance_of_mut_ptr(ptr: *mut T) -> usize
helper method that returns the address and provenance of a mut pointer
Sourcepub fn provenance_of_ref(ptr: &T) -> usize
pub fn provenance_of_ref(ptr: &T) -> usize
helper method that returns the address and provenance of a reference
Sourcepub fn provenance_of_mut(ptr: &mut T) -> usize
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>
impl<T: UniquePointee> AsMut<T> for UniquePointer<T>
Source§impl<T: UniquePointee> AsRef<T> for UniquePointer<T>
impl<T: UniquePointee> AsRef<T> for UniquePointer<T>
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.
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>
fn clone(&self) -> UniquePointer<T>
1.0.0 · Source§fn clone_from(&mut self, source: &Self)
fn clone_from(&mut self, source: &Self)
source
. Read more