-
-
Save rust-play/862fe7541fb5d626aa4463da0edc68cb to your computer and use it in GitHub Desktop.
Code shared from the Rust Playground
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // Creating a heapless vector type using MaybeUninit<T> safely for | |
| // unintialized memory (useful in embedded systems where there's limited | |
| // memory space for heap allocations). | |
| #![no_std] | |
| extern crate std; | |
| use arrayvec::ArrayVec; | |
| mod arrayvec { | |
| use core::mem::MaybeUninit; | |
| #[derive(Debug)] | |
| pub struct ArrayVec<T, const N: usize> { | |
| values: [MaybeUninit<T>; N], | |
| len: usize, | |
| } | |
| impl<T, const N: usize> ArrayVec<T, N> { | |
| /// Creates a new empty ArrayVec | |
| pub fn new() -> Self { | |
| // [MaybeUninit<T>; N] is zero-initialized to uninit by default. | |
| // Meaning the array starts from a blank slate waiting to be | |
| // initialized by `write()`; filling uninit elements with | |
| // "garbage" bytes. | |
| ArrayVec { | |
| // values: unsafe { MaybeUninit::uninit().assume_init() }, | |
| // Same as the commented code above but safer. | |
| values: [const { MaybeUninit::uninit() }; N], | |
| len: 0, | |
| } | |
| } | |
| /// Pushes a value if there's space, returning `Err(value)` if full. | |
| /// Safe: `.write()` takes ownership and marks the slot as | |
| /// initialized. | |
| pub fn try_push(&mut self, value: T) -> Result<(), T> { | |
| if self.len == N { | |
| return Err(value); | |
| } | |
| self.values[self.len].write(value); | |
| self.len += 1; | |
| Ok(()) | |
| } | |
| /// Returns a reference to the element at `index` if within bounds | |
| /// and initialized. | |
| /// SAFETY: Unsafe internally: Assumes first `len` slots are init. | |
| pub fn get(&self, index: usize) -> Option<&T> { | |
| if index >= self.len { | |
| return None; | |
| } | |
| // Same as the commented code below | |
| unsafe { Some(&*self.values[index].as_ptr()) } | |
| // unsafe { Some(self.values[index].assume_init_ref()) } | |
| } | |
| /// Pops the last value if any, returning it (or `None` if empty). | |
| /// SAFETY: Safe: Uses `.assume_init_read()` to extract and mark | |
| /// as uninit. | |
| pub fn pop(&mut self) -> Option<T> { | |
| if self.len == 0 { | |
| return None; | |
| } | |
| self.len -= 1; | |
| Some(unsafe { self.values[self.len].assume_init_read() }) | |
| } | |
| /// Return array of init and uninit elements in ArrayVec. | |
| /// | |
| /// ASIDE: I don't actually need to add `'a` due to Lifetime Ellision | |
| /// rules. I left it just to remind myself how far I've come to | |
| /// perfectly understand lifetimes now. | |
| /// | |
| /// Use `into_arr` as an associated function. | |
| pub fn into_arr<'a>(arr_vec: &'a ArrayVec<T, N>) | |
| -> [Option<&'a T>; N] | |
| { | |
| let mut arr: [Option<&T>; N] = [const { None }; N]; | |
| for i in 0..N { | |
| let el = arr_vec.get(i); | |
| arr[i] = el; | |
| } | |
| arr | |
| } | |
| /// Returns the current length. | |
| pub fn len(&self) -> usize { | |
| self.len | |
| } | |
| } | |
| // Implement Drop trait to safely deallocate initialized elements. | |
| impl<T, const N: usize> Drop for ArrayVec<T, N> { | |
| fn drop(&mut self) { | |
| // Explicity drop the first `len` initialized elements. | |
| for i in 0..self.len { | |
| unsafe { | |
| self.values[i].assume_init_drop(); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| const CAP: usize = 5; | |
| fn main() { | |
| let mut arr_vec1 = ArrayVec::<i32, CAP>::new(); | |
| std::println!("{:?}", arr_vec1); // View init ArrayVec | |
| let mut count; | |
| // Push a few elements | |
| for i in 0..(CAP - 2) { | |
| count = 1 + i as i32; | |
| arr_vec1.try_push(count).unwrap() | |
| } | |
| std::println!("{:?}", arr_vec1); // View pushed elements | |
| // Return element in initialized index; | |
| // if index is not initialized, return None. | |
| let arr_els1 = ArrayVec::into_arr(&arr_vec1); | |
| std::println!("---\nInit values: {:?}", arr_els1); | |
| // Pop from MaybeUninit ArrayVec; if uninit, return None. | |
| std::println!("---\nArrayVec `len` before pop: {}", arr_vec1.len()); | |
| std::println!("Popped value: {:?}", arr_vec1.pop()); | |
| std::println!("ArrayVec `len` after pop: {}", arr_vec1.len()); | |
| // TEST: Add more elements beyond `CAP` size for ArrayVec; | |
| // `try_push` should escape and return with Err. | |
| let arr_len = arr_vec1.len(); | |
| count = *arr_vec1.get(arr_len - 1).unwrap(); | |
| let mut arr_err_els: ArrayVec<Result<(), i32>, CAP> = ArrayVec::new(); | |
| for _ in arr_len..(CAP * 2) { | |
| count += 1; | |
| if let Err(value) = arr_vec1.try_push(count) { | |
| arr_err_els.try_push(Err(value)).unwrap(); | |
| } | |
| } | |
| std::println!( | |
| "---\nFilled ArrayVec: {:?}", ArrayVec::into_arr(&arr_vec1) | |
| ); | |
| std::println!( | |
| "Err beyond ArrayVec: {:?}", ArrayVec::into_arr(&arr_err_els) | |
| ); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment