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 = "basic_traits"
version = "0.1.0"
edition = "2024"
[dependencies]

View File

@@ -0,0 +1,15 @@
# Configuration System Traits
Build a configuration system where different components (database, cache, logger) can be configured uniformly. You'll define traits that allow different types to be configured, validated, and reset in a standard way.
## What you'll practice
- Defining traits that establish contracts between types
- Implementing the same trait for different struct types
- Using traits as function parameters with `impl Trait` syntax
- Creating functions that work with any type implementing specific traits
## Further information
- [Traits: Defining Shared Behavior](https://doc.rust-lang.org/book/ch10-02-traits.html)
- [Traits as Parameters](https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters)

View File

@@ -0,0 +1,119 @@
// TODO: Define a trait called `Configurable` with these methods:
// - configure(&mut self, setting: &str, value: &str) -> Result<(), String>
// - is_valid(&self) -> bool
// - reset(&mut self)
trait Configurable {
// Add your methods here
}
struct DatabaseConfig {
host: String,
port: u16,
max_connections: u32,
}
struct CacheConfig {
size_mb: u32,
ttl_seconds: u64,
enabled: bool,
}
struct LoggerConfig {
level: String,
output_file: Option<String>,
timestamp_format: String,
}
// TODO: Implement Configurable for DatabaseConfig
// configure should handle: "host", "port", "max_connections"
// is_valid should return true if host is not empty and port > 0
// reset should set host="localhost", port=5432, max_connections=10
// TODO: Implement Configurable for CacheConfig
// configure should handle: "size_mb", "ttl_seconds", "enabled"
// is_valid should return true if size_mb > 0 and ttl_seconds > 0
// reset should set size_mb=100, ttl_seconds=300, enabled=true
// TODO: Implement Configurable for LoggerConfig
// configure should handle: "level", "output_file", "timestamp_format"
// is_valid should return true if level is one of: "debug", "info", "warn", "error"
// reset should set level="info", output_file=None, timestamp_format="%Y-%m-%d %H:%M:%S"
// TODO: Write a function `apply_config` that takes any Configurable and a list of (key, value) pairs
// It should try to configure each setting and return a Vec of error messages for failed configurations
fn apply_config(config: &mut impl Configurable, settings: &[(&str, &str)]) -> Vec<String> {
todo!("Apply configuration settings")
}
// TODO: Write a function `validate_and_reset_if_invalid` that takes any Configurable
// If the config is invalid, reset it and return true. If valid, return false.
fn validate_and_reset_if_invalid(config: &mut impl Configurable) -> bool {
todo!("Validate config and reset if invalid")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_database_config() {
let mut db = DatabaseConfig {
host: "".to_string(),
port: 0,
max_connections: 0,
};
assert!(!db.is_valid());
db.reset();
assert!(db.is_valid());
assert_eq!(db.host, "localhost");
assert_eq!(db.port, 5432);
}
#[test]
fn test_cache_configure() {
let mut cache = CacheConfig {
size_mb: 0,
ttl_seconds: 0,
enabled: false,
};
cache.reset();
assert!(cache.is_valid());
assert!(cache.configure("size_mb", "200").is_ok());
assert_eq!(cache.size_mb, 200);
assert!(cache.configure("invalid_key", "value").is_err());
}
#[test]
fn test_logger_validation() {
let mut logger = LoggerConfig {
level: "invalid".to_string(),
output_file: None,
timestamp_format: "".to_string(),
};
assert!(!logger.is_valid());
assert!(validate_and_reset_if_invalid(&mut logger));
assert!(logger.is_valid());
assert_eq!(logger.level, "info");
}
#[test]
fn test_apply_config() {
let mut db = DatabaseConfig {
host: "".to_string(),
port: 0,
max_connections: 0,
};
let settings = [("host", "example.com"), ("port", "3306"), ("invalid", "value")];
let errors = apply_config(&mut db, &settings);
assert_eq!(errors.len(), 1); // Only "invalid" should fail
assert_eq!(db.host, "example.com");
assert_eq!(db.port, 3306);
}
}