Hone logo
Hone
Problems

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 SafeList struct must accept a generic type parameter T.
  • Type Safety: The add method should only accept elements of type T. Attempting to add an element of a different type should result in a compile-time error.
  • add Method: Adds an element of type T to the list.
  • get Method: Retrieves an element at a given index. Should return an Option<T> to handle out-of-bounds access gracefully.
  • len Method: 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 len method should return 0. The get method should return None for any index.
  • Out-of-bounds access: The get method should return None if 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 add method should have a time complexity of O(1) on average.
  • The get method should have a time complexity of O(1).
  • The len method should have a time complexity of O(1).
  • The code must compile without warnings.
  • The code should be idiomatic Rust.

Notes

  • Consider using the Vec type 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.
Loading editor...
rust