generic types, traits and lifetimes

- get claude to create rustlings style exercises based on chapter 10 of the book
- update CLAUDE.md to prevent it from producing incorrect exercises
- complete exercises/01-generic-cache-system
This commit is contained in:
2026-01-22 11:58:33 +00:00
parent f55039411e
commit 02fbf819e4
14 changed files with 567 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
[package]
name = "generic_structs"
version = "0.1.0"
edition = "2024"
[dependencies]

View File

@@ -0,0 +1,15 @@
# Generic Message Queue
Build a message queue system that can handle different types of messages. You'll create generic structs and enums that can queue up emails, notifications, tasks, or any other message type without code duplication.
## What you'll practice
- Defining generic structs that hold collections of any type
- Creating generic enums for different message states
- Writing implementation blocks with generic type parameters
- Using associated functions vs instance methods with generics
## Further information
- [Generic Data Types](https://doc.rust-lang.org/book/ch10-01-syntax.html)
- [Method Definitions](https://doc.rust-lang.org/book/ch05-03-method-syntax.html)

View File

@@ -0,0 +1,102 @@
use std::collections::VecDeque;
// TODO: Make this struct generic over message type T
// It should hold a queue of messages and track total processed count
struct MessageQueue {
queue: VecDeque<String>,
processed_count: usize,
}
// TODO: Make this enum generic over T for message data and E for error data
// Messages can be Pending, Processing, or Failed with error info
enum MessageStatus {
Pending(String),
Processing(String),
Failed(String, String), // message, error
}
// TODO: Create a generic struct for message metadata
// Should store the message of type T and a priority level (u8)
struct PriorityMessage {
content: String,
priority: u8,
}
// TODO: Implement methods for MessageQueue<T>
impl MessageQueue<String> {
// Create new empty queue
fn new() -> Self {
todo!("Create new message queue")
}
// Add message to back of queue
fn enqueue(&mut self, message: String) {
todo!("Add message to queue")
}
// Remove and return next message from front, increment processed count
fn dequeue(&mut self) -> Option<String> {
todo!("Remove next message")
}
// Get current queue length
fn len(&self) -> usize {
todo!("Return queue length")
}
// Get total number of processed messages
fn total_processed(&self) -> usize {
todo!("Return processed count")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_queue_creation() {
let queue: MessageQueue<String> = MessageQueue::new();
assert_eq!(queue.len(), 0);
assert_eq!(queue.total_processed(), 0);
}
#[test]
fn test_enqueue_dequeue() {
let mut queue = MessageQueue::new();
queue.enqueue("First message".to_string());
queue.enqueue("Second message".to_string());
assert_eq!(queue.len(), 2);
assert_eq!(queue.dequeue(), Some("First message".to_string()));
assert_eq!(queue.len(), 1);
assert_eq!(queue.total_processed(), 1);
}
#[test]
fn test_message_status_variants() {
let pending: MessageStatus<String, String> = MessageStatus::Pending("Task 1".to_string());
let failed: MessageStatus<String, String> = MessageStatus::Failed("Task 2".to_string(), "Connection timeout".to_string());
match pending {
MessageStatus::Pending(msg) => assert_eq!(msg, "Task 1"),
_ => panic!("Should be Pending"),
}
}
#[test]
fn test_priority_message() {
let high_priority: PriorityMessage<String> = PriorityMessage {
content: "Urgent task".to_string(),
priority: 1,
};
let low_priority: PriorityMessage<i32> = PriorityMessage {
content: 42,
priority: 5,
};
assert_eq!(high_priority.priority, 1);
assert_eq!(low_priority.content, 42);
}
}