Default Trait Patterns
The Default trait in Rust provides a way to create a default value for a type. This is incredibly useful when you need sensible starting values, especially in configurations, builders, or when using struct update syntax.
The Default trait is defined in the standard library as:
pub trait Default {
fn default() -> Self;
}Many types in the standard library implement Default: numbers default to zero, booleans to false, String to an empty string, Vec to an empty vector, and Option to None. You can derive Default for your own types if all fields implement Default, or implement it manually for custom behavior.
A common pattern is using Default with struct update syntax (..Default::default()) to specify only the fields you care about while letting the rest take their default values. This is particularly powerful in configuration structs and builder patterns.
Your Task
Implement the following types and functions demonstrating the Default trait:
-
AppConfig- A configuration struct withtheme(String),dark_mode(bool),font_size(u32), andmax_connections(usize). DeriveDefaultwhere theme is"light", dark_mode isfalse, font_size is14, and max_connections is100. -
Counter- A simple counter struct with acountfield (i32). ImplementDefaultmanually to start at0. -
BoundedValue<T>- A generic struct withvalue: T,min: T, andmax: T. ImplementDefaultwhereT: Default + Clone, setting all three fields toT::default(). -
Status- An enum with variantsPending,Active,Completed, andFailed. ImplementDefaultto returnStatus::Pending. -
create_with_defaults<T: Default>()- A generic function that creates and returnsT::default(). -
merge_with_defaults(partial: PartialConfig) -> AppConfig- Takes aPartialConfig(with allOptionfields) and returns anAppConfig, using defaults for anyNonevalues. -
default_vec<T: Default>(count: usize) -> Vec<T>- Creates aVeccontainingcountelements, each initialized toT::default().
Example
use default_trait_patterns::*;
// Using derived Default
let config = AppConfig::default();
assert_eq!(config.theme, "light");
assert_eq!(config.dark_mode, false);
assert_eq!(config.font_size, 14);
// Struct update syntax with Default
let custom = AppConfig {
dark_mode: true,
..Default::default()
};
assert!(custom.dark_mode);
assert_eq!(custom.theme, "light"); // Still default
// Manual Default implementation
let counter = Counter::default();
assert_eq!(counter.count, 0);
// Generic with Default bounds
let bounded: BoundedValue<i32> = BoundedValue::default();
assert_eq!(bounded.value, 0);
// Enum default
let status = Status::default();
assert!(matches!(status, Status::Pending));
// Generic function
let s: String = create_with_defaults();
assert_eq!(s, "");
// Merging with defaults
let partial = PartialConfig {
theme: Some("dark".to_string()),
dark_mode: None,
font_size: Some(16),
max_connections: None,
};
let merged = merge_with_defaults(partial);
assert_eq!(merged.theme, "dark");
assert!(!merged.dark_mode); // Used default
assert_eq!(merged.font_size, 16);
assert_eq!(merged.max_connections, 100); // Used default
// Creating default-initialized vectors
let zeros: Vec<i32> = default_vec(5);
assert_eq!(zeros, vec![0, 0, 0, 0, 0]);Hints
Click here to reveal hints
- To derive
Defaultwith custom values, you'll need to implement the trait manually rather than using#[derive(Default)] - For
AppConfig, implementDefaultmanually to set specific default values - The
#[derive(Default)]macro works when all fields implementDefaultand you want their default values - Use
Option::unwrap_or_else(|| default_value)for efficient default fallbacks inmerge_with_defaults - For
default_vec, you can usestd::iter::repeat_withor a simple loop - Remember that
Cloneis needed if you want to use the same default value multiple times inBoundedValue
Run to execute your code and see test results here.