Skip to content

Commit

Permalink
Merge pull request #8 from orxfun/self-referential-elements
Browse files Browse the repository at this point in the history
self-referential-elements module is created
  • Loading branch information
orxfun committed Jan 2, 2024
2 parents 280a5ac + 8b14de6 commit fdea967
Show file tree
Hide file tree
Showing 9 changed files with 406 additions and 99 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "orx-pinned-vec"
version = "0.5.3"
version = "1.0.0"
edition = "2021"
authors = ["orxfun <orx.ugur.arikan@gmail.com>"]
description = "`PinnedVec` trait defines the interface for vectors which guarantee that the elements are pinned to their memory locations unless explicitly changed by the caller."
Expand Down
29 changes: 25 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

A `PinnedVec` guarantees that positions of its elements are not changed implicitly. Note that `std::vec::Vec` does not satisfy this requirement.

[`SplitVec`](https://crates.io/crates/orx-split-vec) and [`FixedVec`](https://crates.io/crates/orx-fixed-vec) are two efficient implementations.
[`SplitVec`](https://crates.io/crates/orx-split-vec) and [`FixedVec`](https://crates.io/crates/orx-fixed-vec) are two efficient implementations.

## B. Motivation

Expand All @@ -25,8 +25,29 @@ This crate suggests the following approach:
* Referencing each other will be through the natural `&` way rather than requiring any of the smart pointers.
* In terms of convenience, building the collection will be close to building a regular vector.

## C. Self-Referential-Collection Element Traits

## C. Safety
This crate also defines under the `orx_pinned_vec::self_referential_elements` module the required traits to enable building self referential collections with thin references.

* `SelfRefNext` trait simply requires:
* `fn next(&self) -> Option<&'a Self>;` and
* `fn set_next(&mut self, next: Option<&'a Self>);` methods.

`SelfRefPrev` is the previous counterpart.

Notice that these two traits are sufficient to define a linked list. [`orx_linked_list::LinkedList`](https://crates.io/crates/orx-linked-list) implements `SelfRefPrev` and `SelfRefNext` to conveniently define a recurisve doubly linked list.

Further, there exist multiple reference counterparts. They are useful in defining relations such as the *children* of a tree node or *heads of outgoing arcs* from a graph node, etc. There exist *vec* variants to be used for holding variable number of references. However, there also exist constant sized array versions which are useful in structures such as binary search trees where the number of references is bounded by a const.

The table below presents the complete list of traits which suffice to define all aforementioned relations:

| | prev | next |
|---------------------------------------------|----------------|----------------|
| single reference | SelfRefPrev | SelfRefNext |
| dynamic number of references | SelfRefPrevVec | SelfRefNextVec |
| multiple elements with a `const` max-length | SelfRefPrevArr | SelfRefNextArr |

## D. Safety

In order to safely build a self-referential collection, we have two requirements

Expand Down Expand Up @@ -65,7 +86,7 @@ Finally, assume the tree we are trying to build is:
```


## C.1. Safety - Immutable Push
## D.1. Safety - Immutable Push

We need to be able to push to the vector with an immutable reference. It might (will) sound counter-intuitive. We will first discuss why it is necessary and then why it is okay (opinionated).

Expand Down Expand Up @@ -161,7 +182,7 @@ This relation allows for a clear separation of the building stage. For instance,
* build the self referential collection using the immutable push operation (build)
* unwrap the `ImpVec` and get back the built data structure as `V` (leave the building phase)

## C.2. Safety - Non-growing Mutations & Clone
## D.2. Safety - Non-growing Mutations & Clone

With self referential collections, some other mutating methods can lead to critical problems as well. These are the methods which change positions of already pushed elements or remove elements from the vector:

Expand Down
39 changes: 29 additions & 10 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,41 @@
//! There might be various situations where pinned elements are helpful.
//!
//! * It is somehow required for async code, following [blog](https://blog.cloudflare.com/pin-and-unpin-in-rust) could be useful for the interested.
//! * It is a requirement to represent self-referential types with thin references.
//! * It is crucial in representing self-referential types with thin references.
//!
//! This crate focuses more on the latter. Particularly, it aims to make it safely and conveniently possible to build **self-referential collections** such as linked list, tree or graph.
//! This crate focuses more on the latter. Particularly, it aims to make it safe and convenient to build **performant self-referential collections** such as linked lists, trees or graphs.
//!
//! As explained in rust-docs [here](https://doc.rust-lang.org/std/pin/index.html), there exists types `Pin` and `Unpin` for this very purpose. Through the theoretical discussions, one can easily agree on the safety. However, it is hard to consider the solution as a convenient one with all words `PhantomPinned`, `NonNull`, `dangling`, `Box::pin`, etc. which are alien to the self-referential data structures we are trying to build.
//! As explained in rust-docs [here](https://doc.rust-lang.org/std/pin/index.html), there exist types `Pin` and `Unpin` for this very purpose. Through the theoretical discussions, one can easily agree on the safety. However, the solution is complicated with all words `PhantomPinned`, `NonNull`, `dangling`, `Box::pin`, etc. which are alien to the self-referential data structures we are trying to build.
//!
//! This crate suggests the following approach:
//!
//! * Instances of the self-referential type will be collected together in a vector.
//! * Referencing each other will be through the natural `&` way rather than requiring any of the smart pointers.
//! * In terms of convenience, building the collection will be close to building a regular vector.
//!
//! ## C. Self-Referential-Collection Element Traits
//!
//! ## C. Safety
//! This crate also defines under the `orx_pinned_vec::self_referential_elements` module the required traits to enable building self referential collections with thin references.
//!
//! * `SelfRefNext` trait simply requires:
//! * `fn next(&self) -> Option<&'a Self>;` and
//! * `fn set_next(&mut self, next: Option<&'a Self>);` methods.
//!
//! `SelfRefPrev` is the previous counterpart.
//!
//! Notice that these two traits are sufficient to define a linked list. [`orx_linked_list::LinkedList`](https://crates.io/crates/orx-linked-list) implements `SelfRefPrev` and `SelfRefNext` to conveniently define a recurisve doubly linked list.
//!
//! Further, there exist multiple reference counterparts. They are useful in defining relations such as the *children* of a tree node or *heads of outgoing arcs* from a graph node, etc. There exist *vec* variants to be used for holding variable number of references. However, there also exist constant sized array versions which are useful in structures such as binary search trees where the number of references is bounded by a const.
//!
//! The table below presents the complete list of traits which suffice to define all aforementioned relations:
//!
//! | | prev | next |
//! |---------------------------------------------|----------------|----------------|
//! | single reference | SelfRefPrev | SelfRefNext |
//! | dynamic number of references | SelfRefPrevVec | SelfRefNextVec |
//! | multiple elements with a `const` max-length | SelfRefPrevArr | SelfRefNextArr |
//!
//! ## D. Safety
//!
//! In order to safely build a self-referential collection, we have two requirements
//!
Expand Down Expand Up @@ -65,7 +86,7 @@
//! ```
//!
//!
//! ## C.1. Safety - Immutable Push
//! ## D.1. Safety - Immutable Push
//!
//! We need to be able to push to the vector with an immutable reference. It might (will) sound counter-intuitive. We will first discuss why it is necessary and then why it is okay (opinionated).
//!
Expand All @@ -76,7 +97,6 @@
//! # data: T,
//! # children: Vec<&'a Node<'a, T>>,
//! # }
//! #
//! # impl<'a, T> Node<'a, T> {
//! # fn new(data: T, children: Vec<&'a Node<'a, T>>) -> Self {
//! # Self { data, children }
Expand All @@ -95,7 +115,6 @@
//! # data: T,
//! # children: Vec<&'a Node<'a, T>>,
//! # }
//! #
//! # impl<'a, T> Node<'a, T> {
//! # fn new(data: T, children: Vec<&'a Node<'a, T>>) -> Self {
//! # Self { data, children }
Expand Down Expand Up @@ -181,7 +200,7 @@
//! * build the self referential collection using the immutable push operation (build)
//! * unwrap the `ImpVec` and get back the built data structure as `V` (leave the building phase)
//!
//! ## C.2. Safety - Non-growing Mutations & Clone
//! ## D.2. Safety - Non-growing Mutations & Clone
//!
//! With self referential collections, some other mutating methods can lead to critical problems as well. These are the methods which change positions of already pushed elements or remove elements from the vector:
//!
Expand Down Expand Up @@ -226,11 +245,11 @@
mod not_self_ref;
mod pinned_vec;
mod pinned_vec_simple;
mod self_ref;
/// Traits to define variants of self-referential-collection elements.
pub mod self_referential_elements;
/// Utility functions to make PinnedVec implementations more convenient.
pub mod utils;

pub use not_self_ref::NotSelfRefVecItem;
pub use pinned_vec::PinnedVec;
pub use pinned_vec_simple::PinnedVecSimple;
pub use self_ref::SelfRefVecItem;
7 changes: 7 additions & 0 deletions src/pinned_vec.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,11 @@ use std::fmt::{Debug, Formatter, Result};
pub trait PinnedVec<T> {
/// Iterator yielding references to the elements of the vector.
type Iter<'a>: Iterator<Item = &'a T>
where
T: 'a,
Self: 'a;
/// Iterator yielding mutable references to the elements of the vector.
type IterMut<'a>: Iterator<Item = &'a mut T>
where
T: 'a,
Self: 'a;
Expand Down Expand Up @@ -260,6 +265,8 @@ pub trait PinnedVec<T> {

/// Returns an iterator to elements of the vector.
fn iter(&self) -> Self::Iter<'_>;
/// Returns an iterator of mutable references to elements of the vector.
fn iter_mut(&mut self) -> Self::IterMut<'_>;

// required for common trait implementations
/// This method tests for `self` and `other` values to be equal, and is used by `==`.
Expand Down
84 changes: 0 additions & 84 deletions src/self_ref.rs

This file was deleted.

7 changes: 7 additions & 0 deletions src/self_referential_elements/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
mod next_prev;
mod next_prev_arr;
mod next_prev_vec;

pub use next_prev::{SelfRefNext, SelfRefPrev};
pub use next_prev_arr::{SelfRefNextArr, SelfRefPrevArr};
pub use next_prev_vec::{SelfRefNextVec, SelfRefPrevVec};
109 changes: 109 additions & 0 deletions src/self_referential_elements/next_prev.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/// Trait for elements that optionally holds a reference to a `next` element.
///
/// A common example is the linked-list where each node holds a reference to the next node.
///
/// Notice that the trait itself has a life time `'a` which is equal to the lifetime of the potential next elements.
/// This is on purpose as the underlying goal of having these traits it to build self-referential-collections
/// where each element live together and share the same lifetime.
///
/// # Example
///
/// ```rust
/// use orx_pinned_vec::self_referential_elements::*;
///
/// #[derive(PartialEq, Debug)]
/// struct LinkedListNode<'a, Data> {
/// data: Data,
/// next: Option<&'a Self>,
/// prev: Option<&'a Self>,
/// }
///
/// impl<'a, Data> SelfRefNext<'a> for LinkedListNode<'a, Data> {
/// fn next(&self) -> Option<&'a Self> {
/// self.next
/// }
/// fn set_next(&mut self, next: Option<&'a Self>) {
/// self.next = next;
/// }
/// }
///
/// let mut a = LinkedListNode {
/// data: 'a',
/// next: None,
/// prev: None,
/// };
/// assert_eq!(a.next(), None);
///
/// let b = LinkedListNode {
/// data: 'b',
/// next: None,
/// prev: None,
/// };
/// a.set_next(Some(&b));
/// assert_eq!(a.next(), Some(&b));
///
/// a.set_next(None);
/// assert_eq!(a.next(), None);
/// ```
pub trait SelfRefNext<'a> {
/// Reference to the next element of this element; None if this element does not have a next.
fn next(&self) -> Option<&'a Self>;

/// Sets next of this element to the given `next`, which is a reference to the element to be linked.
fn set_next(&mut self, next: Option<&'a Self>);
}

/// Trait for elements that optionally holds a reference to a `prev` element.
///
/// A common example is the doubly-linked-list where each node holds a reference to the prev node.
///
/// Notice that the trait itself has a life time `'a` which is equal to the lifetime of the potential previous elements.
/// This is on purpose as the underlying goal of having these traits it to build self-referential-collections
/// where each element live together and share the same lifetime.
///
/// # Example
///
/// ```rust
/// use orx_pinned_vec::self_referential_elements::*;
///
/// #[derive(PartialEq, Debug)]
/// struct LinkedListNode<'a, Data> {
/// data: Data,
/// next: Option<&'a Self>,
/// prev: Option<&'a Self>,
/// }
///
/// impl<'a, Data> SelfRefPrev<'a> for LinkedListNode<'a, Data> {
/// fn prev(&self) -> Option<&'a Self> {
/// self.prev
/// }
/// fn set_prev(&mut self, prev: Option<&'a Self>) {
/// self.prev = prev;
/// }
/// }
///
/// let mut a = LinkedListNode {
/// data: 'a',
/// next: None,
/// prev: None,
/// };
/// assert_eq!(a.prev(), None);
///
/// let b = LinkedListNode {
/// data: 'b',
/// next: None,
/// prev: None,
/// };
/// a.set_prev(Some(&b));
/// assert_eq!(a.prev(), Some(&b));
///
/// a.set_prev(None);
/// assert_eq!(a.prev(), None);
/// ```
pub trait SelfRefPrev<'a> {
/// Reference to the next element of this element; None if this element does not have a next.
fn prev(&self) -> Option<&'a Self>;

/// Sets next of this element to the given `next`, which is a reference to the element to be linked.
fn set_prev(&mut self, next: Option<&'a Self>);
}
Loading

0 comments on commit fdea967

Please sign in to comment.