From fd23befbdbbf45717cc93d0a076fffd6b9a21e0d Mon Sep 17 00:00:00 2001 From: Ayush Date: Fri, 6 Feb 2026 14:55:09 +0530 Subject: [PATCH 1/7] feat: initial implementation of MiniVec Signed-off-by: Ayush --- src/lib.rs | 666 ++++++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 585 insertions(+), 81 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 5277d08..d04d02d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,54 +1,85 @@ -use std::alloc::{self, Layout}; -use std::mem::ManuallyDrop; -use std::ops::{Deref, DerefMut}; -use std::ptr::NonNull; -use std::{mem, ptr}; +//! A minimal `Vec`-like collection implemented with manual allocation. +//! +//! This crate provides `MiniVec`, a small, educational reimplementation of +//! `std::vec::Vec` following patterns from the Rustonomicon. It's intended +//! for learning and small use-cases, not as a drop-in replacement for `Vec`. +//! +//! # Examples +//! ``` +//! use minivec::MiniVec; +//! +//! let mut v = MiniVec::new(); +//! v.push(1); +//! v.push(2); +//! assert_eq!(&*v, &[1, 2]); +//! assert_eq!(v.pop(), Some(2)); +//! ``` +//! +//! See individual method docs for more examples. +use std::{ + alloc::{self, Layout}, + marker::PhantomData, + mem, + ops::{Deref, DerefMut}, + ptr::{self, NonNull}, +}; -pub struct Vec { +struct RawVec { ptr: NonNull, cap: usize, - len: usize, } -unsafe impl Send for Vec {} -unsafe impl Sync for Vec {} +unsafe impl Send for RawVec {} +unsafe impl Sync for RawVec {} -impl Vec { - pub fn new() -> Self { - assert!(mem::size_of::() != 0, "Can't handle ZSTs yet"); - Vec { +impl RawVec { + fn new() -> Self { + let cap = if mem::size_of::() == 0 { + usize::MAX + } else { + 0 + }; + + // `NonNull::dangling` doubles as "unallocated" and "zero-sized allocation" + RawVec { ptr: NonNull::dangling(), - cap: 0, - len: 0, + cap: cap, } } -} -impl Vec { fn grow(&mut self) { + // since we set the capacity to usize::MAX when T has size 0, getting + // to here means Vec is overfull. + assert!(mem::size_of::() != 0, "capacity overflow"); + let (new_cap, new_layout) = if self.cap == 0 { - (1, Layout::array::(1)) + (1, Layout::array::(1).unwrap()) } else { - // shouldn't overflow since self.cap <= isize::MAX + // `new_cap` will not overflow because `self.cap <= isize::MAX`. let new_cap = 2 * self.cap; - (new_cap, Layout::array::(new_cap)) + + // `Layout::array` checks that the number of bytes is <= usize::MAX, + // but this is redundant since old_layout.size() <= isize::MAX, + // `unwrap` should never fail. + let new_layout = Layout::array::(new_cap).unwrap(); + (new_cap, new_layout) }; - // `Layout::array` checks that the number of bytes allocated is in 1..=isize::MAX - // and will error otherwise. An allocation of 0 bytes isn't possible because of the - // above condition. - let new_layout = new_layout.expect("Allocation too large"); + // Ensure that the new allocation doesn't overflow; <= isize::MAX bytes. + assert!( + new_layout.size() <= isize::MAX as usize, + "Allocation too large" + ); let new_ptr = if self.cap == 0 { unsafe { alloc::alloc(new_layout) } } else { let old_layout = Layout::array::(self.cap).unwrap(); let old_ptr = self.ptr.as_ptr() as *mut u8; - unsafe { alloc::realloc(old_ptr, old_layout, new_layout.size()) } }; - // new_ptr will be null if allocation fails, abort + // new_ptr becomes null if allocation fails, abort. self.ptr = match NonNull::new(new_ptr as *mut T) { Some(p) => p, None => alloc::handle_alloc_error(new_layout), @@ -57,157 +88,630 @@ impl Vec { self.cap = new_cap; } + fn grow_to(&mut self, new_cap: usize) { + if mem::size_of::() == 0 { + return; + } + if new_cap <= self.cap { + return; + } + + let new_layout = Layout::array::(new_cap).unwrap(); + assert!( + new_layout.size() <= isize::MAX as usize, + "Allocation too large" + ); + + let new_ptr = if self.cap == 0 { + unsafe { alloc::alloc(new_layout) } + } else { + let old_layout = Layout::array::(self.cap).unwrap(); + let old_tr = self.ptr.as_ptr() as *mut u8; + unsafe { alloc::realloc(old_tr, old_layout, new_layout.size() as usize) } + }; + + self.ptr = match NonNull::new(new_ptr as *mut T) { + Some(p) => p, + None => alloc::handle_alloc_error(new_layout), + }; + + self.cap = new_cap; + } +} + +impl Drop for RawVec { + fn drop(&mut self) { + if self.cap != 0 && std::mem::size_of::() > 0 { + let layout = std::alloc::Layout::array::(self.cap).unwrap(); + unsafe { + std::alloc::dealloc(self.ptr.as_ptr() as *mut _, layout); + } + } + } +} + +/// A minimal growable contiguous vector. +/// +/// `MiniVec` stores elements in a heap buffer and supports a small set of +/// `Vec`-like operations. Use the methods below to manipulate the collection. +/// +/// # Examples +/// +/// ``` +/// use minivec::MiniVec; +/// +/// let mut v = MiniVec::new(); +/// v.push(10); +/// v.push(20); +/// assert_eq!(v.len(), 2); +/// assert!(!v.is_empty()); +/// assert!(v.capacity() >= 2); +/// ``` +pub struct MiniVec { + buf: RawVec, + len: usize, +} + +unsafe impl Send for MiniVec {} +unsafe impl Sync for MiniVec {} + +impl MiniVec { + fn ptr(&self) -> *mut T { + self.buf.ptr.as_ptr() + } + + fn cap(&self) -> usize { + self.buf.cap + } + + /// Creates a new, empty `MiniVec`. + /// + /// # Examples + /// + /// ``` + /// use minivec::MiniVec; + /// let v: MiniVec = MiniVec::new(); + /// assert_eq!(v.len(), 0); + /// ``` + pub fn new() -> Self { + MiniVec { + buf: RawVec::new(), + len: 0, + } + } + + /// Returns the number of elements in the vector. + /// + /// # Examples + /// + /// ``` + /// use minivec::MiniVec; + /// let mut v = MiniVec::new(); + /// v.push(1); + /// assert_eq!(v.len(), 1); + /// ``` + pub fn len(&self) -> usize { + self.len + } + + /// Returns `true` if the vector contains no elements. + /// + /// # Examples + /// + /// ``` + /// use minivec::MiniVec; + /// let mut v = MiniVec::new(); + /// assert!(v.is_empty()); + /// v.push(1); + /// assert!(!v.is_empty()); + /// ``` + pub fn is_empty(&self) -> bool { + self.len == 0 + } + + /// Returns the number of elements the `MiniVec` can hold without reallocating. + /// + /// Note: capacity may be larger than the number of elements. Calling + /// `reserve` or `reserve_exact` increases capacity. + /// + /// # Examples + /// + /// ``` + /// use minivec::MiniVec; + /// let mut v: MiniVec = MiniVec::new(); + /// v.reserve(5); + /// assert!(v.capacity() >= 5); + /// ``` + pub fn capacity(&self) -> usize { + self.cap() + } + + /// Clears the vector, removing all values. + /// + /// This drops each element in the vector. + /// + /// # Examples + /// + /// ``` + /// use minivec::MiniVec; + /// let mut v = MiniVec::new(); + /// v.push(1); + /// v.clear(); + /// assert!(v.is_empty()); + /// ``` + pub fn clear(&mut self) { + while let Some(_) = self.pop() {} + } + + /// Returns a slice containing all elements of the vector. + /// + /// # Examples + /// + /// ``` + /// use minivec::MiniVec; + /// let mut v = MiniVec::new(); + /// v.push(1); + /// assert_eq!(v.as_slice(), &[1]); + /// ``` + pub fn as_slice(&self) -> &[T] { + &*self + } + + /// Returns a mutable slice containing all elements of the vector. + /// + /// # Examples + /// + /// ``` + /// use minivec::MiniVec; + /// let mut v = MiniVec::new(); + /// v.push(1); + /// v.as_mut_slice()[0] = 2; + /// assert_eq!(v.as_slice(), &[2]); + /// ``` + pub fn as_mut_slice(&mut self) -> &mut [T] { + &mut *self + } + + /// Ensures the `MiniVec` can hold at least `additional` more elements without reallocating. + /// + /// This grows capacity exponentially where necessary. + /// + /// # Examples + /// + /// ``` + /// use minivec::MiniVec; + /// let mut v: MiniVec = MiniVec::new(); + /// v.reserve(10); + /// assert!(v.capacity() >= 10); + /// ``` + pub fn reserve(&mut self, additional: usize) { + let required = self.len.checked_add(additional).expect("capacity overflow"); + if self.cap() >= required { + return; + } + while self.cap() < required { + self.buf.grow(); + } + } + + /// Ensures the `MiniVec` has capacity for exactly `additional` more elements (no fewer). + /// + /// Unlike `reserve`, this attempts to allocate the exact requested capacity in one go. + /// + /// # Examples + /// + /// ``` + /// use minivec::MiniVec; + /// let mut v: MiniVec = MiniVec::new(); + /// v.reserve_exact(3); + /// assert!(v.capacity() >= 3); + /// ``` + pub fn reserve_exact(&mut self, additional: usize) { + let required = self.len.checked_add(additional).expect("capacity overflow"); + if self.cap() >= required { + return; + } + self.buf.grow_to(required); + } + + /// Appends an element to the back of the collection. + /// + /// # Examples + /// + /// ``` + /// use minivec::MiniVec; + /// let mut v = MiniVec::new(); + /// v.push(5); + /// assert_eq!(v.len(), 1); + /// assert_eq!(v.as_slice(), &[5]); + /// ``` pub fn push(&mut self, elem: T) { - if self.len == self.cap { - self.grow(); + if self.len == self.cap() { + self.buf.grow(); } unsafe { - ptr::write(self.ptr.as_ptr().add(self.len), elem); + ptr::write(self.ptr().add(self.len), elem); } // This operation will not fail, we will get OOM first. self.len += 1; } + /// Removes the last element and returns it, or `None` if empty. + /// + /// # Examples + /// + /// ``` + /// use minivec::MiniVec; + /// let mut v = MiniVec::new(); + /// v.push(10); + /// assert_eq!(v.pop(), Some(10)); + /// assert_eq!(v.pop(), None); + /// ``` pub fn pop(&mut self) -> Option { if self.len == 0 { None } else { self.len -= 1; - unsafe { Some(ptr::read(self.ptr.as_ptr().add(self.len))) } + unsafe { Some(ptr::read(self.ptr().add(self.len))) } } } + /// Inserts an element at `index`, shifting elements to the right. + /// + /// # Panics + /// + /// Panics if `index > len`. + /// + /// # Examples + /// + /// ``` + /// use minivec::MiniVec; + /// let mut v = MiniVec::new(); + /// v.push(1); + /// v.push(3); + /// v.insert(1, 2); + /// assert_eq!(v.as_slice(), &[1, 2, 3]); + /// ``` pub fn insert(&mut self, index: usize, elem: T) { assert!(index <= self.len, "index out of bounds"); - if self.len == self.cap { - self.grow(); + if self.len == self.cap() { + self.buf.grow(); } unsafe { ptr::copy( - self.ptr.as_ptr().add(index), - self.ptr.as_ptr().add(index + 1), + self.ptr().add(index), + self.ptr().add(index + 1), self.len - index, ); - ptr::write(self.ptr.as_ptr().add(index), elem); + ptr::write(self.ptr().add(index), elem); } self.len += 1; } + /// Removes and returns the element at `index`, shifting elements left. + /// + /// # Panics + /// + /// Panics if `index >= len`. + /// + /// # Examples + /// + /// ``` + /// use minivec::MiniVec; + /// let mut v = MiniVec::new(); + /// v.push(1); + /// v.push(2); + /// assert_eq!(v.remove(0), 1); + /// assert_eq!(v.as_slice(), &[2]); + /// ``` pub fn remove(&mut self, index: usize) -> T { assert!(index < self.len, "index out of bounds"); unsafe { self.len -= 1; - let result = ptr::read(self.ptr.as_ptr().add(index)); + let result = ptr::read(self.ptr().add(index)); ptr::copy( - self.ptr.as_ptr().add(index + 1), - self.ptr.as_ptr().add(index), + self.ptr().add(index + 1), + self.ptr().add(index), self.len - index, ); result } } + + /// Removes all elements and returns an iterator that yields the removed elements. + /// + /// The vector's length is set to zero immediately; elements are yielded by the returned iterator. + /// + /// # Examples + /// + /// ``` + /// use minivec::MiniVec; + /// let mut v = MiniVec::new(); + /// v.push(10); + /// v.push(20); + /// let drained: Vec<_> = v.drain().collect(); + /// assert_eq!(drained, vec![10, 20]); + /// assert!(v.is_empty()); + /// ``` + pub fn drain(&'_ mut self) -> Drain<'_, T> { + let iter = unsafe { RawValIter::new(&self) }; + + self.len = 0; + + Drain { + iter, + vec: PhantomData, + } + } } -impl Drop for Vec { +impl Drop for MiniVec { fn drop(&mut self) { if self.len != 0 { + // deallocation is handled by RawVec while let Some(_) = self.pop() {} - let layout = Layout::array::(self.cap).unwrap(); - unsafe { - alloc::dealloc(self.ptr.as_ptr() as *mut u8, layout); - } } } } -impl Deref for Vec { +impl Deref for MiniVec { type Target = [T]; fn deref(&self) -> &[T] { - unsafe { std::slice::from_raw_parts(self.ptr.as_ptr(), self.len) } + unsafe { std::slice::from_raw_parts(self.ptr(), self.len) } } } -impl DerefMut for Vec { +impl DerefMut for MiniVec { fn deref_mut(&mut self) -> &mut [T] { - unsafe { std::slice::from_raw_parts_mut(self.ptr.as_ptr(), self.len) } + unsafe { std::slice::from_raw_parts_mut(self.ptr(), self.len) } } } -pub struct IntoIter { - buf: NonNull, - cap: usize, +struct RawValIter { start: *const T, end: *const T, } -impl IntoIterator for Vec { - type Item = T; - type IntoIter = IntoIter; - fn into_iter(self) -> IntoIter { - let vec = ManuallyDrop::new(self); - - let ptr = vec.ptr; - let cap = vec.cap; - let len = vec.len; - - IntoIter { - buf: ptr, - cap, - start: ptr.as_ptr(), - end: if cap == 0 { - ptr.as_ptr() +impl RawValIter { + // unsafe construct because it has no associated lifetimes. This is + // necessary to store RawValIter as in the same struct as its actual + // allocation. OK to use since it's a private implementation detail. + unsafe fn new(slice: &[T]) -> Self { + RawValIter { + start: slice.as_ptr(), + end: if mem::size_of::() == 0 { + ((slice.as_ptr() as usize) + slice.len()) as *const _ + } else if slice.len() == 0 { + slice.as_ptr() } else { - unsafe { ptr.as_ptr().add(len) } + unsafe { slice.as_ptr().add(slice.len()) } }, } } } -impl Iterator for IntoIter { +impl Iterator for RawValIter { type Item = T; fn next(&mut self) -> Option { if self.start == self.end { None } else { unsafe { - let result = ptr::read(self.start); - self.start = self.start.offset(1); - Some(result) + if mem::size_of::() == 0 { + self.start = (self.start as usize + 1) as *const _; + Some(ptr::read(NonNull::::dangling().as_ptr())) + } else { + let old_ptr = self.start; + self.start = self.start.offset(1); + Some(ptr::read(old_ptr)) + } } } } fn size_hint(&self) -> (usize, Option) { - let len = (self.end as usize - self.start as usize) / mem::size_of::(); + let elem_size = mem::size_of::(); + let len = + (self.end as usize - self.start as usize) / if elem_size == 0 { 1 } else { elem_size }; + (len, Some(len)) } } -impl DoubleEndedIterator for IntoIter { +impl DoubleEndedIterator for RawValIter { fn next_back(&mut self) -> Option { if self.start == self.end { None } else { unsafe { - self.end = self.end.offset(-1); - Some(ptr::read(self.end)) + if mem::size_of::() == 0 { + self.end = (self.end as usize - 1) as *const _; + Some(ptr::read(NonNull::::dangling().as_ptr())) + } else { + self.end = self.end.offset(-1); + Some(ptr::read(self.end)) + } } } } } +/// Iterator that yields values by value when consuming a `MiniVec`. +/// +/// # Examples +/// +/// ``` +/// use minivec::MiniVec; +/// let mut v = MiniVec::new(); +/// v.push(1); +/// v.push(2); +/// let out: Vec<_> = v.into_iter().collect(); +/// assert_eq!(out, vec![1, 2]); +/// ``` +pub struct IntoIter { + _buf: RawVec, + iter: RawValIter, +} + +impl Iterator for IntoIter { + type Item = T; + fn next(&mut self) -> Option { + self.iter.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl DoubleEndedIterator for IntoIter { + fn next_back(&mut self) -> Option { + self.iter.next_back() + } +} + impl Drop for IntoIter { fn drop(&mut self) { - if self.cap != 0 { - for _ in &mut *self {} - let layout = Layout::array::(self.cap).unwrap(); - unsafe { - alloc::dealloc(self.buf.as_ptr() as *mut u8, layout); - } + for _ in &mut *self {} + } +} + +impl IntoIterator for MiniVec { + type Item = T; + type IntoIter = IntoIter; + fn into_iter(self) -> IntoIter { + unsafe { + let iter = RawValIter::new(&self); + + let buf = ptr::read(&self.buf); + mem::forget(self); + + IntoIter { _buf: buf, iter } } } } + +/// An iterator produced by `MiniVec::drain`. +/// +/// Iterates over and yields the drained elements by value. +/// +/// # Examples +/// +/// ``` +/// use minivec::MiniVec; +/// let mut v = MiniVec::new(); +/// v.push(1); +/// v.push(2); +/// let drained: Vec<_> = v.drain().collect(); +/// assert_eq!(drained, vec![1, 2]); +/// ``` +pub struct Drain<'a, T: 'a> { + vec: PhantomData<&'a mut MiniVec>, + iter: RawValIter, +} + +impl<'a, T> Iterator for Drain<'a, T> { + type Item = T; + fn next(&mut self) -> Option { + self.iter.next() + } + + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +impl<'a, T> DoubleEndedIterator for Drain<'a, T> { + fn next_back(&mut self) -> Option { + self.iter.next_back() + } +} + +impl<'a, T> Drop for Drain<'a, T> { + fn drop(&mut self) { + for _ in &mut *self {} + } +} + +#[cfg(test)] +mod tests { + use crate::MiniVec; + + #[test] + fn push_pop_roundtrip() { + let mut v = MiniVec::new(); + v.push(1); + v.push(2); + v.push(3); + + assert_eq!(v.pop(), Some(3)); + assert_eq!(v.pop(), Some(2)); + assert_eq!(v.pop(), Some(1)); + assert_eq!(v.pop(), None); + } + + #[test] + fn insert_remove() { + let mut v = MiniVec::new(); + v.push(1); + v.push(3); + v.insert(1, 2); + + assert_eq!(&*v, &[1, 2, 3]); + assert_eq!(v.remove(1), 2); + assert_eq!(&*v, &[1, 3]); + } + + #[test] + fn drain_consumes_elements() { + let mut v = MiniVec::new(); + v.push(10); + v.push(20); + v.push(30); + + let drained: Vec<_> = v.drain().collect(); + assert_eq!(drained, vec![10, 20, 30]); + assert_eq!(&*v, &[]); + } + + #[test] + fn into_iter_works() { + let mut v = MiniVec::new(); + v.push(1); + v.push(2); + v.push(3); + + let collected: Vec<_> = v.into_iter().collect(); + assert_eq!(collected, vec![1, 2, 3]); + } + + #[test] + fn reserve_and_capacity() { + let mut v: MiniVec = MiniVec::new(); + v.reserve(5); + assert!(v.capacity() >= 5); + v.reserve_exact(10); + assert!(v.capacity() >= 10); + } + + #[test] + fn clear_works() { + let mut v = MiniVec::new(); + v.push(1); + v.clear(); + assert!(v.is_empty()); + } + + #[test] + fn as_slice_and_mut() { + let mut v = MiniVec::new(); + v.push(1); + assert_eq!(v.as_slice(), &[1]); + v.as_mut_slice()[0] = 2; + assert_eq!(v.as_slice(), &[2]); + } +} From fb7c049764a32dde27e9c10d5423673306513bda Mon Sep 17 00:00:00 2001 From: Ayush Date: Fri, 6 Feb 2026 14:55:27 +0530 Subject: [PATCH 2/7] examples: add an example file demonstrating basic usage of MiniVec Signed-off-by: Ayush --- examples/basics.rs | 52 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 examples/basics.rs diff --git a/examples/basics.rs b/examples/basics.rs new file mode 100644 index 0000000..226e726 --- /dev/null +++ b/examples/basics.rs @@ -0,0 +1,52 @@ +use minivec::MiniVec; + +fn main() { + // Basic push/pop + println!(">>> Executing: push(1); push(2)"); + let mut v = MiniVec::new(); + v.push(1); + v.push(2); + println!("initial: {:?}", v.as_slice()); + println!(); + + // Reserve capacity + println!(">>> Executing: reserve(8) and then print len/capacity"); + v.reserve(8); + println!("len = {}, capacity = {}", v.len(), v.capacity()); + println!(); + + // Insert / remove + println!(">>> Executing: insert(1, 5); remove(1)"); + v.insert(1, 5); + println!("after insert: {:?}", v.as_slice()); + let removed = v.remove(1); + println!("removed element = {}", removed); + println!(); + + // Mutate via as_mut_slice + println!(">>> Executing: as_mut_slice()[0] = 42"); + v.as_mut_slice()[0] = 42; + println!("after mutation: {:?}", v.as_slice()); + println!(); + + // Drain elements + println!(">>> Executing: push(100); drain()"); + v.push(100); + let drained: Vec<_> = v.drain().collect(); + println!("drained: {:?}", drained); + println!( + "after drain: len = {}, capacity = {}", + v.len(), + v.capacity() + ); + println!(); + + // Consume via into_iter + println!(">>> Executing: into_iter()"); + let mut v2 = MiniVec::new(); + v2.push(10); + v2.push(20); + let collected: Vec<_> = v2.into_iter().collect(); + println!("into_iter collected: {:?}", collected); + println!(); +} From 874031f934e79d55f7dff6a61816d857035d00bf Mon Sep 17 00:00:00 2001 From: Ayush Date: Fri, 6 Feb 2026 15:24:02 +0530 Subject: [PATCH 3/7] CI: add github actions workflow for ci Signed-off-by: Ayush --- .github/workflows/CI.yml | 42 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 .github/workflows/CI.yml diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml new file mode 100644 index 0000000..466b55c --- /dev/null +++ b/.github/workflows/CI.yml @@ -0,0 +1,42 @@ +name: CI pipeline + +on: + push: + branches: [master] + pull_request: + branches: [master] + +env: + CARGO_TERM_COLOR: always + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + - name: Build and test + run: cargo test --verbose + + clippy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + components: clippy + - name: Run clippy + run: cargo clippy --all-targets -- -D warnings + + fmt: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Install Rust + uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + components: rustfmt + - name: Run rustfmt + run: cargo fmt --all -- --check From aae02d500222f88fbad759bb0f7b4983db15706d Mon Sep 17 00:00:00 2001 From: Ayush Date: Fri, 6 Feb 2026 15:24:29 +0530 Subject: [PATCH 4/7] fix(clippy): fix clippy warnings Signed-off-by: Ayush --- src/lib.rs | 20 +++++++++++++------- 1 file changed, 13 insertions(+), 7 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index d04d02d..cedaf27 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -43,7 +43,7 @@ impl RawVec { // `NonNull::dangling` doubles as "unallocated" and "zero-sized allocation" RawVec { ptr: NonNull::dangling(), - cap: cap, + cap, } } @@ -107,7 +107,7 @@ impl RawVec { } else { let old_layout = Layout::array::(self.cap).unwrap(); let old_tr = self.ptr.as_ptr() as *mut u8; - unsafe { alloc::realloc(old_tr, old_layout, new_layout.size() as usize) } + unsafe { alloc::realloc(old_tr, old_layout, new_layout.size()) } }; self.ptr = match NonNull::new(new_ptr as *mut T) { @@ -155,6 +155,12 @@ pub struct MiniVec { unsafe impl Send for MiniVec {} unsafe impl Sync for MiniVec {} +impl Default for MiniVec { + fn default() -> Self { + Self::new() + } +} + impl MiniVec { fn ptr(&self) -> *mut T { self.buf.ptr.as_ptr() @@ -240,7 +246,7 @@ impl MiniVec { /// assert!(v.is_empty()); /// ``` pub fn clear(&mut self) { - while let Some(_) = self.pop() {} + while self.pop().is_some() {} } /// Returns a slice containing all elements of the vector. @@ -254,7 +260,7 @@ impl MiniVec { /// assert_eq!(v.as_slice(), &[1]); /// ``` pub fn as_slice(&self) -> &[T] { - &*self + self } /// Returns a mutable slice containing all elements of the vector. @@ -438,7 +444,7 @@ impl MiniVec { /// assert!(v.is_empty()); /// ``` pub fn drain(&'_ mut self) -> Drain<'_, T> { - let iter = unsafe { RawValIter::new(&self) }; + let iter = unsafe { RawValIter::new(self) }; self.len = 0; @@ -453,7 +459,7 @@ impl Drop for MiniVec { fn drop(&mut self) { if self.len != 0 { // deallocation is handled by RawVec - while let Some(_) = self.pop() {} + while self.pop().is_some() {} } } } @@ -485,7 +491,7 @@ impl RawValIter { start: slice.as_ptr(), end: if mem::size_of::() == 0 { ((slice.as_ptr() as usize) + slice.len()) as *const _ - } else if slice.len() == 0 { + } else if slice.is_empty() { slice.as_ptr() } else { unsafe { slice.as_ptr().add(slice.len()) } From 46bfc7bd24abfc8f2a6dea54cb9b066c328dcdbc Mon Sep 17 00:00:00 2001 From: Ayush Date: Fri, 6 Feb 2026 16:21:05 +0530 Subject: [PATCH 5/7] ci: add release workflow, rename test workflow to build Signed-off-by: Ayush --- .github/workflows/CI.yml | 2 +- .github/workflows/release.yml | 53 +++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 466b55c..ea84e0b 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -10,7 +10,7 @@ env: CARGO_TERM_COLOR: always jobs: - test: + build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..a14ea84 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,53 @@ +name: Release-plz + +on: + push: + branches: + - main + +jobs: + + # Release unpublished packages. + release: + name: Release-plz release + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - &checkout + name: Checkout repository + uses: actions/checkout@v6 + with: + fetch-depth: 0 + persist-credentials: false + - &install-rust + name: Install Rust toolchain + uses: dtolnay/rust-toolchain@stable + - name: Run release-plz + uses: release-plz/action@v0.5 + with: + command: release + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + + # Create a PR with the new versions and changelog, preparing the next release. + release-plz-pr: + name: Release-plz PR + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + concurrency: + group: release-plz-${{ github.ref }} + cancel-in-progress: false + steps: + - *checkout + - *install-rust + - name: Run release-plz + uses: release-plz/action@v0.5 + with: + command: release-pr + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} From 27cb2dc453041ddceb16e12aa17edf20b8b96103 Mon Sep 17 00:00:00 2001 From: Ayush Date: Fri, 6 Feb 2026 16:21:47 +0530 Subject: [PATCH 6/7] chore: rename crate to lessvec and update metadata Signed-off-by: Ayush --- Cargo.lock | 2 +- Cargo.toml | 11 +++- README.md | 20 +++++++ examples/basics.rs | 6 +-- src/lib.rs | 126 ++++++++++++++++++++++----------------------- 5 files changed, 97 insertions(+), 68 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ac78129..552cf6b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,5 +3,5 @@ version = 4 [[package]] -name = "minivec" +name = "lessvec" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index 6505ecf..72f676e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,9 +1,18 @@ [package] -name = "minivec" +name = "lessvec" version = "0.1.0" edition = "2024" description = "A custom Vec implementation using the Rust standard library." +authors = ["Ayush Chauhan (bakayu) "] readme = "README.md" +categories = ["data-structures", "collections"] license = "MIT" +repository = "https://github.com/bakayu/lessvec.git" +exclude = ["target", ".github"] +homepage = "https://github.com/bakayu/lessvec" +documentation = "https://docs.rs/lessvec" [dependencies] + +[package.metadata.docs.rs] +all-features = true diff --git a/README.md b/README.md index e69de29..021353c 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,20 @@ +# lessvec + +A minimal, educational Vec-like collection implemented with only the Rust standard library. + +[![crates.io](https://img.shields.io/crates/v/lessvec.svg)](https://crates.io/crates/lessvec) [![docs.rs](https://docs.rs/lessvec/badge.svg)](https://docs.rs/lessvec) [![License: MIT](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE) [![Build Status](https://github.com/OWNER/REPO/actions/workflows/CI.yml/badge.svg)](https://github.com/OWNER/REPO/actions/workflows/CI.yml) [![GitHub tag](https://img.shields.io/github/v/tag/OWNER/REPO.svg)](https://github.com/OWNER/REPO/releases) + +## Quick example + +```rust +use lessvec::LessVec; + +let mut v = LessVec::new(); +v.push(1); +v.push(2); +assert_eq!(v.as_slice(), &[1, 2]); +``` + +## License + +[MIT LICENSE](./LICENSE) diff --git a/examples/basics.rs b/examples/basics.rs index 226e726..3ee8879 100644 --- a/examples/basics.rs +++ b/examples/basics.rs @@ -1,9 +1,9 @@ -use minivec::MiniVec; +use lessvec::LessVec; fn main() { // Basic push/pop println!(">>> Executing: push(1); push(2)"); - let mut v = MiniVec::new(); + let mut v = LessVec::new(); v.push(1); v.push(2); println!("initial: {:?}", v.as_slice()); @@ -43,7 +43,7 @@ fn main() { // Consume via into_iter println!(">>> Executing: into_iter()"); - let mut v2 = MiniVec::new(); + let mut v2 = LessVec::new(); v2.push(10); v2.push(20); let collected: Vec<_> = v2.into_iter().collect(); diff --git a/src/lib.rs b/src/lib.rs index cedaf27..15a8e8d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,14 +1,14 @@ //! A minimal `Vec`-like collection implemented with manual allocation. //! -//! This crate provides `MiniVec`, a small, educational reimplementation of +//! This crate provides `LessVec`, a small, educational reimplementation of //! `std::vec::Vec` following patterns from the Rustonomicon. It's intended //! for learning and small use-cases, not as a drop-in replacement for `Vec`. //! //! # Examples //! ``` -//! use minivec::MiniVec; +//! use lessvec::LessVec; //! -//! let mut v = MiniVec::new(); +//! let mut v = LessVec::new(); //! v.push(1); //! v.push(2); //! assert_eq!(&*v, &[1, 2]); @@ -132,36 +132,36 @@ impl Drop for RawVec { /// A minimal growable contiguous vector. /// -/// `MiniVec` stores elements in a heap buffer and supports a small set of +/// `LessVec` stores elements in a heap buffer and supports a small set of /// `Vec`-like operations. Use the methods below to manipulate the collection. /// /// # Examples /// /// ``` -/// use minivec::MiniVec; +/// use lessvec::LessVec; /// -/// let mut v = MiniVec::new(); +/// let mut v = LessVec::new(); /// v.push(10); /// v.push(20); /// assert_eq!(v.len(), 2); /// assert!(!v.is_empty()); /// assert!(v.capacity() >= 2); /// ``` -pub struct MiniVec { +pub struct LessVec { buf: RawVec, len: usize, } -unsafe impl Send for MiniVec {} -unsafe impl Sync for MiniVec {} +unsafe impl Send for LessVec {} +unsafe impl Sync for LessVec {} -impl Default for MiniVec { +impl Default for LessVec { fn default() -> Self { Self::new() } } -impl MiniVec { +impl LessVec { fn ptr(&self) -> *mut T { self.buf.ptr.as_ptr() } @@ -170,17 +170,17 @@ impl MiniVec { self.buf.cap } - /// Creates a new, empty `MiniVec`. + /// Creates a new, empty `LessVec`. /// /// # Examples /// /// ``` - /// use minivec::MiniVec; - /// let v: MiniVec = MiniVec::new(); + /// use lessvec::LessVec; + /// let v: LessVec = LessVec::new(); /// assert_eq!(v.len(), 0); /// ``` pub fn new() -> Self { - MiniVec { + LessVec { buf: RawVec::new(), len: 0, } @@ -191,8 +191,8 @@ impl MiniVec { /// # Examples /// /// ``` - /// use minivec::MiniVec; - /// let mut v = MiniVec::new(); + /// use lessvec::LessVec; + /// let mut v = LessVec::new(); /// v.push(1); /// assert_eq!(v.len(), 1); /// ``` @@ -205,8 +205,8 @@ impl MiniVec { /// # Examples /// /// ``` - /// use minivec::MiniVec; - /// let mut v = MiniVec::new(); + /// use lessvec::LessVec; + /// let mut v = LessVec::new(); /// assert!(v.is_empty()); /// v.push(1); /// assert!(!v.is_empty()); @@ -215,7 +215,7 @@ impl MiniVec { self.len == 0 } - /// Returns the number of elements the `MiniVec` can hold without reallocating. + /// Returns the number of elements the `LessVec` can hold without reallocating. /// /// Note: capacity may be larger than the number of elements. Calling /// `reserve` or `reserve_exact` increases capacity. @@ -223,8 +223,8 @@ impl MiniVec { /// # Examples /// /// ``` - /// use minivec::MiniVec; - /// let mut v: MiniVec = MiniVec::new(); + /// use lessvec::LessVec; + /// let mut v: LessVec = LessVec::new(); /// v.reserve(5); /// assert!(v.capacity() >= 5); /// ``` @@ -239,8 +239,8 @@ impl MiniVec { /// # Examples /// /// ``` - /// use minivec::MiniVec; - /// let mut v = MiniVec::new(); + /// use lessvec::LessVec; + /// let mut v = LessVec::new(); /// v.push(1); /// v.clear(); /// assert!(v.is_empty()); @@ -254,8 +254,8 @@ impl MiniVec { /// # Examples /// /// ``` - /// use minivec::MiniVec; - /// let mut v = MiniVec::new(); + /// use lessvec::LessVec; + /// let mut v = LessVec::new(); /// v.push(1); /// assert_eq!(v.as_slice(), &[1]); /// ``` @@ -268,8 +268,8 @@ impl MiniVec { /// # Examples /// /// ``` - /// use minivec::MiniVec; - /// let mut v = MiniVec::new(); + /// use lessvec::LessVec; + /// let mut v = LessVec::new(); /// v.push(1); /// v.as_mut_slice()[0] = 2; /// assert_eq!(v.as_slice(), &[2]); @@ -278,15 +278,15 @@ impl MiniVec { &mut *self } - /// Ensures the `MiniVec` can hold at least `additional` more elements without reallocating. + /// Ensures the `LessVec` can hold at least `additional` more elements without reallocating. /// /// This grows capacity exponentially where necessary. /// /// # Examples /// /// ``` - /// use minivec::MiniVec; - /// let mut v: MiniVec = MiniVec::new(); + /// use lessvec::LessVec; + /// let mut v: LessVec = LessVec::new(); /// v.reserve(10); /// assert!(v.capacity() >= 10); /// ``` @@ -300,15 +300,15 @@ impl MiniVec { } } - /// Ensures the `MiniVec` has capacity for exactly `additional` more elements (no fewer). + /// Ensures the `LessVec` has capacity for exactly `additional` more elements (no fewer). /// /// Unlike `reserve`, this attempts to allocate the exact requested capacity in one go. /// /// # Examples /// /// ``` - /// use minivec::MiniVec; - /// let mut v: MiniVec = MiniVec::new(); + /// use lessvec::LessVec; + /// let mut v: LessVec = LessVec::new(); /// v.reserve_exact(3); /// assert!(v.capacity() >= 3); /// ``` @@ -325,8 +325,8 @@ impl MiniVec { /// # Examples /// /// ``` - /// use minivec::MiniVec; - /// let mut v = MiniVec::new(); + /// use lessvec::LessVec; + /// let mut v = LessVec::new(); /// v.push(5); /// assert_eq!(v.len(), 1); /// assert_eq!(v.as_slice(), &[5]); @@ -349,8 +349,8 @@ impl MiniVec { /// # Examples /// /// ``` - /// use minivec::MiniVec; - /// let mut v = MiniVec::new(); + /// use lessvec::LessVec; + /// let mut v = LessVec::new(); /// v.push(10); /// assert_eq!(v.pop(), Some(10)); /// assert_eq!(v.pop(), None); @@ -373,8 +373,8 @@ impl MiniVec { /// # Examples /// /// ``` - /// use minivec::MiniVec; - /// let mut v = MiniVec::new(); + /// use lessvec::LessVec; + /// let mut v = LessVec::new(); /// v.push(1); /// v.push(3); /// v.insert(1, 2); @@ -407,8 +407,8 @@ impl MiniVec { /// # Examples /// /// ``` - /// use minivec::MiniVec; - /// let mut v = MiniVec::new(); + /// use lessvec::LessVec; + /// let mut v = LessVec::new(); /// v.push(1); /// v.push(2); /// assert_eq!(v.remove(0), 1); @@ -435,8 +435,8 @@ impl MiniVec { /// # Examples /// /// ``` - /// use minivec::MiniVec; - /// let mut v = MiniVec::new(); + /// use lessvec::LessVec; + /// let mut v = LessVec::new(); /// v.push(10); /// v.push(20); /// let drained: Vec<_> = v.drain().collect(); @@ -455,7 +455,7 @@ impl MiniVec { } } -impl Drop for MiniVec { +impl Drop for LessVec { fn drop(&mut self) { if self.len != 0 { // deallocation is handled by RawVec @@ -464,14 +464,14 @@ impl Drop for MiniVec { } } -impl Deref for MiniVec { +impl Deref for LessVec { type Target = [T]; fn deref(&self) -> &[T] { unsafe { std::slice::from_raw_parts(self.ptr(), self.len) } } } -impl DerefMut for MiniVec { +impl DerefMut for LessVec { fn deref_mut(&mut self) -> &mut [T] { unsafe { std::slice::from_raw_parts_mut(self.ptr(), self.len) } } @@ -546,13 +546,13 @@ impl DoubleEndedIterator for RawValIter { } } -/// Iterator that yields values by value when consuming a `MiniVec`. +/// Iterator that yields values by value when consuming a `LessVec`. /// /// # Examples /// /// ``` -/// use minivec::MiniVec; -/// let mut v = MiniVec::new(); +/// use lessvec::LessVec; +/// let mut v = LessVec::new(); /// v.push(1); /// v.push(2); /// let out: Vec<_> = v.into_iter().collect(); @@ -586,7 +586,7 @@ impl Drop for IntoIter { } } -impl IntoIterator for MiniVec { +impl IntoIterator for LessVec { type Item = T; type IntoIter = IntoIter; fn into_iter(self) -> IntoIter { @@ -601,22 +601,22 @@ impl IntoIterator for MiniVec { } } -/// An iterator produced by `MiniVec::drain`. +/// An iterator produced by `LessVec::drain`. /// /// Iterates over and yields the drained elements by value. /// /// # Examples /// /// ``` -/// use minivec::MiniVec; -/// let mut v = MiniVec::new(); +/// use lessvec::LessVec; +/// let mut v = LessVec::new(); /// v.push(1); /// v.push(2); /// let drained: Vec<_> = v.drain().collect(); /// assert_eq!(drained, vec![1, 2]); /// ``` pub struct Drain<'a, T: 'a> { - vec: PhantomData<&'a mut MiniVec>, + vec: PhantomData<&'a mut LessVec>, iter: RawValIter, } @@ -645,11 +645,11 @@ impl<'a, T> Drop for Drain<'a, T> { #[cfg(test)] mod tests { - use crate::MiniVec; + use crate::LessVec; #[test] fn push_pop_roundtrip() { - let mut v = MiniVec::new(); + let mut v = LessVec::new(); v.push(1); v.push(2); v.push(3); @@ -662,7 +662,7 @@ mod tests { #[test] fn insert_remove() { - let mut v = MiniVec::new(); + let mut v = LessVec::new(); v.push(1); v.push(3); v.insert(1, 2); @@ -674,7 +674,7 @@ mod tests { #[test] fn drain_consumes_elements() { - let mut v = MiniVec::new(); + let mut v = LessVec::new(); v.push(10); v.push(20); v.push(30); @@ -686,7 +686,7 @@ mod tests { #[test] fn into_iter_works() { - let mut v = MiniVec::new(); + let mut v = LessVec::new(); v.push(1); v.push(2); v.push(3); @@ -697,7 +697,7 @@ mod tests { #[test] fn reserve_and_capacity() { - let mut v: MiniVec = MiniVec::new(); + let mut v: LessVec = LessVec::new(); v.reserve(5); assert!(v.capacity() >= 5); v.reserve_exact(10); @@ -706,7 +706,7 @@ mod tests { #[test] fn clear_works() { - let mut v = MiniVec::new(); + let mut v = LessVec::new(); v.push(1); v.clear(); assert!(v.is_empty()); @@ -714,7 +714,7 @@ mod tests { #[test] fn as_slice_and_mut() { - let mut v = MiniVec::new(); + let mut v = LessVec::new(); v.push(1); assert_eq!(v.as_slice(), &[1]); v.as_mut_slice()[0] = 2; From 8b070d826b1cef102de2251dacc41054f289b492 Mon Sep 17 00:00:00 2001 From: Ayush Date: Fri, 6 Feb 2026 16:22:12 +0530 Subject: [PATCH 7/7] ci: fix branch name in release workflow Signed-off-by: Ayush --- .github/workflows/release.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a14ea84..7fad178 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -3,7 +3,7 @@ name: Release-plz on: push: branches: - - main + - master jobs: