Rust Vec Basics: Building a Dynamic Array
This challenge will test your understanding of Rust's Vec type, the standard library's dynamic array implementation. You will create a simple data structure that mimics some of Vec's core functionalities, allowing you to store and manipulate a collection of elements that can grow. This is a fundamental building block for many data-intensive applications.
Problem Description
Your task is to implement a struct named MyVec<T> that acts as a simplified version of std::vec::Vec<T>. This MyVec should be able to store elements of any type T. You need to implement the following core functionalities:
- Initialization: Create an empty
MyVec. - Adding Elements: Append new elements to the end of the
MyVec. - Accessing Elements: Retrieve elements by their index.
- Getting Length: Determine the number of elements currently stored.
- Removing Elements: Remove the last element from the
MyVec.
Key Requirements:
MyVec<T>struct: Define a structMyVec<T>that encapsulates the data. Internally, it should use a standard RustVecto manage the underlying storage.new()function: Implement an associated functionnew()that returns an emptyMyVec<T>.push(value: T)method: Implement a methodpushthat takes a value of typeTand adds it to the end of theMyVec.get(index: usize) -> Option<&T>method: Implement a methodgetthat takes an index and returns a reference to the element at that index wrapped inOption. If the index is out of bounds, it should returnNone.len()method: Implement a methodlenthat returns the current number of elements in theMyVecas ausize.pop() -> Option<T>method: Implement a methodpopthat removes and returns the last element from theMyVecwrapped inOption. If theMyVecis empty, it should returnNone.
Expected Behavior:
- When
new()is called, an emptyMyVecshould be created with a length of 0. push()should increase the length of theMyVecand store the provided value.get()should correctly return a reference to the element at the specified index, orNonefor invalid indices.len()should accurately report the number of elements.pop()should remove and return the last element, decreasing the length, or returnNoneif empty.
Edge Cases to Consider:
- Accessing an index out of bounds with
get(). - Calling
pop()on an emptyMyVec. - Storing different types of data (e.g., integers, strings, custom structs).
Examples
Example 1:
let mut my_vec: MyVec<i32> = MyVec::new();
my_vec.push(10);
my_vec.push(20);
my_vec.push(30);
println!("Length: {}", my_vec.len()); // Expected: Length: 3
println!("Element at index 1: {:?}", my_vec.get(1)); // Expected: Element at index 1: Some(20)
Explanation: We create a new MyVec for integers, push three values, and then check its length and retrieve an element by its index.
Example 2:
let mut my_vec: MyVec<String> = MyVec::new();
my_vec.push("hello".to_string());
my_vec.push("world".to_string());
println!("Length before pop: {}", my_vec.len()); // Expected: Length before pop: 2
println!("Popped element: {:?}", my_vec.pop()); // Expected: Popped element: Some("world")
println!("Length after pop: {}", my_vec.len()); // Expected: Length after pop: 1
println!("Element at index 0: {:?}", my_vec.get(0)); // Expected: Element at index 0: Some("hello")
println!("Element at index 1: {:?}", my_vec.get(1)); // Expected: Element at index 1: None
Explanation: We demonstrate pushing strings, checking the length, popping the last element, and verifying the updated length and element accessibility.
Example 3: Edge Cases
let mut my_vec: MyVec<f64> = MyVec::new();
println!("Initial length: {}", my_vec.len()); // Expected: Initial length: 0
println!("Pop from empty: {:?}", my_vec.pop()); // Expected: Pop from empty: None
println!("Get at index 0: {:?}", my_vec.get(0)); // Expected: Get at index 0: None
my_vec.push(3.14);
println!("Get at index 1: {:?}", my_vec.get(1)); // Expected: Get at index 1: None
Explanation: This example covers the behavior when operating on an empty MyVec and attempting to access an out-of-bounds index after adding an element.
Constraints
- Your
MyVec<T>struct must usestd::vec::Vec<T>internally for storage. - The
getmethod must returnOption<&T>. - The
popmethod must returnOption<T>. - All methods should be implemented within an
impl<T> MyVec<T> { ... }block. - No external crates are allowed, only the Rust standard library.
Notes
- Remember to derive
DebugforMyVecif you want to print it directly for debugging purposes (though the examples focus on specific method outputs). - Consider how
Optionis used to handle cases where an element might not exist or a operation might fail (like popping from an empty vector). - This exercise is about understanding composition and implementing simple interfaces. You are wrapping
std::vec::Vec, not reimplementing its memory management or growth strategy.