Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@ edition = "2021"
authors = ["raa <raa@riseup.net>"]
description = "Custom data structures crate for Rust"
license = "MIT"
repository = "https://github.com/raa-dev/rust-ds"
repository = "https://github.com/raa-dev/rust-ds"
categories = ["data structures", "algorithms"]
23 changes: 20 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,28 @@
# Data Structures Crate
[<img alt="github" src="https://img.shields.io/badge/github-raa%E2%80%93dev/rust%E2%80%93ds-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/raa-dev/rust-ds)
[<img alt="crates.io" src="https://img.shields.io/crates/v/rust-ds.svg?style=for-the-badge&color=fc8d62&logo=rust" height="20">](https://crates.io/crates/rust-ds)
[<img alt="docs.rs" src="https://img.shields.io/badge/docs.rs-rust%E2%80%93ds-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs" height="20">](https://docs.rs/rust-ds)

This is a library of data structures implemented in Rust. The goal of this project is to provide a collection of data structures that are not available in the Rust standard library. The library is designed to be easy to use and efficient.

This crate still in early stage of development, but the intended objective is to provide the following data structures:
* [Linked List](src/linked_list/README.md)
* [Double Linked List](src/double_linked_list/README.md)
* [Linked List](src/linked_lists/README.md)
* [Double Linked List](src/linked_lists/README.md)
* [Hash Table](src/hash_table/README.md)
* Stack
* Queue
* Tree
* Graph
* Heap
* Heap

## Usage
Add this dependency to your `Cargo.toml`
```toml
[dependencies]
rust-ds = "0.1.1"
```

## License

Licensed under
- MIT license ([LICENSE-MIT](https://github.com/Amanieu/parking_lot/blob/HEAD/LICENSE-MIT) or https://opensource.org/licenses/MIT)
42 changes: 42 additions & 0 deletions src/hash_table/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Hash Table

A Hash Table orders the data as pairs of keys and values. This enables
efficient data retrieval based on the key. Each key is hashed to produce
an index at which the corresponding value is stored. This allows for
average-case constant time complexity, O(1), for both insertion and
lookup operations.

## Operations
- **Insert**: Add a key-value pair to the hash table.
- **Remove**: Remove a key-value pair from the hash table.
- **Get**: Retrieve the value associated with a given key.
- **Update**: Modify the value associated with a given key.
- **Resize**: Adjust the size of the hash table to maintain efficient operations.

## Usage

```rust
use rust_ds::hash_table::Table;

fn main() {
let mut table = Table::new(4); // You can use default instead to use a 64 length capacity

// Insert key-value pairs
table.insert("key1", "value1");
table.insert("key2", "value2");

// Retrieve a value
if let Some(value) = table.get("key1") {
println!("The value for 'key1' is {}", value);
}

// Update a value
table.update("key1", "new_value1");

// Remove a key-value pair
table.remove("key2");

// Resize the hash table
table.resize(32);
}
```
24 changes: 24 additions & 0 deletions src/hash_table/errors.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
use std::fmt::{Debug, Display, Formatter, Result as FmtResult};

pub type Result<T> = core::result::Result<T, Error>;

#[derive(Debug)]
pub enum Error {
EmptyTable,
KeyNotFound,
InvalidCapacity,
}

impl Display for Error {
fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
match self {
Error::EmptyTable => write!(f, "Operation failed: Table is empty"),
Error::KeyNotFound => {
write!(f, "Operation failed: Key not found in table")
}
Error::InvalidCapacity => {
write!(f, "Operation failed: Invalid capacity")
}
}
}
}
133 changes: 133 additions & 0 deletions src/hash_table/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
mod errors;

pub(super) use errors::{Error, Result};
use std::collections::hash_map::DefaultHasher;
use std::fmt::Debug;
use std::hash::{Hash, Hasher};

type KeyPointer<K, V> = Option<(K, V)>;

/// `Table` is a simple hash table implementation.
#[derive(Debug)]
pub struct Table<K, V>
where
K: Clone,
V: Clone,
{
pub elements: Vec<KeyPointer<K, V>>,
capacity: usize,
}

impl<K, V> Table<K, V>
where
K: Hash + Eq + Debug + Clone,
V: Debug + Clone,
{
/// Create a new `Table` with the given capacity.
pub fn new(capacity: usize) -> Self {
Self {
elements: vec![None; capacity],
capacity,
}
}
/// Hash the key and return the index.
fn hash<Q>(&self, key: &Q) -> usize
where
K: std::borrow::Borrow<Q>,
Q: Hash + ?Sized,
{
let mut hasher = DefaultHasher::new();
key.hash(&mut hasher);
(hasher.finish() as usize) % self.capacity
}
/// Insert a new key-value pair into the table.
pub fn insert(&mut self, key: K, value: V) {
let index = self.hash(&key);
self.elements[index] = Some((key, value));
}
/// Get the value for the given key.
pub fn get(&self, key: &K) -> Result<&V> {
let index = self.hash(key);
self.elements[index]
.as_ref()
.map(|(_, v)| v)
.ok_or(Error::KeyNotFound)
}
/// Remove the key-value pair from the table.
pub fn remove(&mut self, key: &K) -> Result<V> {
if self.capacity == 0 {
return Err(Error::EmptyTable);
}
let index = self.hash(key);
if let Some((_, value)) = self.elements[index].take() {
Ok(value)
} else {
Err(Error::KeyNotFound)
}
}
/// Update the value for the given key.
pub fn update(&mut self, key: &K) -> Result<&mut V> {
if self.capacity == 0 {
return Err(Error::EmptyTable);
}
let index = self.hash(key);
self.elements[index]
.as_mut()
.map(|(_, v)| v)
.ok_or(Error::KeyNotFound)
}
/// Resize the table to the new capacity.
pub fn resize(&mut self, new_capacity: usize) -> Result<()> {
if new_capacity == 0 {
return Err(Error::InvalidCapacity);
}

let mut new_table = Table::new(new_capacity);
for element in self.elements.iter().filter_map(|e| e.as_ref()) {
new_table.insert(element.0.clone(), element.1.clone());
}
self.elements = new_table.elements;
self.capacity = new_capacity;
Ok(())
}
}
/// Default implementation for `Table`.
impl<K, V> Default for Table<K, V>
where
K: Hash + Eq + Debug + Clone,
V: Debug + Clone,
{
fn default() -> Self {
Self::new(64)
}
}

// region: --- Tests
#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test_hash_table_ops() {
let mut table = Table::new(16);
table.insert("key1", "value1");
assert_eq!(table.get(&"key1").unwrap(), &"value1");
table.remove(&"key1").unwrap();
assert!(table.get(&"key1").is_err());
table.insert("key2", "value2");
if let Ok(v) = table.update(&"key2") {
*v = "value3";
}
assert_eq!(table.get(&"key2").unwrap(), &"value3");
}

#[test]
fn test_hash_table_errors() {
let mut table: Table<&str, &str> = Table::new(16);
assert!(table.get(&"key1").is_err());
assert!(table.remove(&"key1").is_err());
assert!(table.update(&"key1").is_err());
assert!(table.resize(0).is_err());
}
}
// endregion: --- Tests
1 change: 1 addition & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
//!
//! `rust_ds` is a collection of data structures utilities to use in Rust.

pub mod hash_table;
pub mod linked_lists;
59 changes: 59 additions & 0 deletions src/linked_lists/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,62 @@ The following operations are defined for a linked list:
- **Search**: Searches for an element in the list.
- **Update**: Updates an element in the list.
- **Get**: Returns the element at a given index.

## Usage
### Singly Linked List Example

```rust
use linked_lists::Singly;

fn main() {
let mut list = Singly::new();
list.append(1);
list.append(2);
list.append(3);
println!("List after appending elements: {:?}", list);

list.pop();
println!("List after popping an element: {:?}", list);

list.remove(1);
println!("List after removing an element: {:?}", list);

let found = list.search(2);
println!("Element 2 found: {}", found);

list.update(0, 4);
println!("List after updating an element: {:?}", list);

let element = list.get(0);
println!("Element at index 0: {:?}", element);
}
```

### Doubly Linked List Example

```rust
use linked_lists::Double;

fn main() {
let mut list = Double::new();
list.append(1);
list.append(2);
list.append(3);
println!("List after appending elements: {:?}", list);

list.pop();
println!("List after popping an element: {:?}", list);

list.remove(1);
println!("List after removing an element: {:?}", list);

let found = list.search(2);
println!("Element 2 found: {}", found);

list.update(0, 4);
println!("List after updating an element: {:?}", list);

let element = list.get(0);
println!("Element at index 0: {:?}", element);
}
```
Loading