Safe Data Structures with Generics and Traits in Rust
Rust's type system is a cornerstone of its safety guarantees. This challenge focuses on leveraging Rust's generics and traits to create a safe and reusable data structure – a SafeList. The goal is to build a list-like structure that enforces type safety at compile time, preventing runtime errors related to incorrect data types.
Problem Description
You are tasked with implementing a SafeList struct in Rust. This struct should hold a collection of elements, but with the crucial constraint that all elements within the list must be of the same type. The SafeList should provide methods for adding elements, retrieving elements, and determining the list's length. The type of the elements should be parameterized using generics, ensuring that the compiler enforces type consistency.
Key Requirements:
- Generic Type Parameter: The
SafeListstruct must accept a generic type parameterT. - Type Safety: The
addmethod should only accept elements of typeT. Attempting to add an element of a different type should result in a compile-time error. addMethod: Adds an element of typeTto the list.getMethod: Retrieves an element at a given index. Should return anOption<T>to handle out-of-bounds access gracefully.lenMethod: Returns the number of elements in the list.- Internal Storage: Use a
Vec<T>internally to store the elements.
Expected Behavior:
The SafeList should behave like a basic list, but with the added guarantee that all elements are of the same type. The compiler should catch any attempts to violate this type constraint. The get method should return None if the index is out of bounds.
Edge Cases to Consider:
- Empty list: The
lenmethod should return 0. Thegetmethod should returnNonefor any index. - Out-of-bounds access: The
getmethod should returnNoneif the index is greater than or equal to the list's length.
Examples
Example 1:
Input:
let mut safe_list: SafeList<i32> = SafeList::new();
safe_list.add(1);
safe_list.add(2);
safe_list.add(3);
Output:
safe_list.len() == 3
safe_list.get(1) == Some(2)
safe_list.get(5) == None
Explanation: A SafeList of i32 is created and populated. The length is 3, the element at index 1 is 2, and accessing an out-of-bounds index returns None.
Example 2:
Input:
let mut safe_list: SafeList<String> = SafeList::new();
safe_list.add("hello".to_string());
safe_list.add("world".to_string());
Output:
safe_list.len() == 2
safe_list.get(0) == Some("hello")
Explanation: A SafeList of String is created and populated. The length is 2, and the element at index 0 is "hello".
Example 3: (Edge Case)
Input:
let safe_list: SafeList<i32> = SafeList::new();
Output:
safe_list.len() == 0
safe_list.get(0) == None
Explanation: An empty SafeList is created. The length is 0, and accessing any index returns None.
Constraints
- The
addmethod should have a time complexity of O(1) on average. - The
getmethod should have a time complexity of O(1). - The
lenmethod should have a time complexity of O(1). - The code must compile without warnings.
- The code should be idiomatic Rust.
Notes
- Consider using the
Vectype from the standard library for internal storage. - Rust's generics and traits are key to achieving type safety.
- The
Option<T>type is useful for handling cases where a value might be absent (e.g., out-of-bounds access). - Think about how to best leverage Rust's ownership and borrowing rules to ensure memory safety.
- Focus on creating a clear, concise, and well-documented solution.