Chapter 4: Practical Programming Patterns
Chapter Status: π’ 67% Working (6/9 examples)
| Status | Count | Examples |
|---|---|---|
| β Working | 6 | Validated with 7-layer testing |
| π― Tested | 6 | All basic patterns work perfectly |
| β³ Untested | 3 | Advanced features (arrays, mut, String construction) |
| π Requires Implementation | 3 | Waiting for compiler features |
Last updated: 2025-10-30 Ruchy version: ruchy v3.149.0
Working Examples (6/9) - 100% Pass Rate:
- Example 1: Calculator with if/else β
- Example 2: User validation (string .len(), .contains()) β
- Example 3: Score processing (type casting to f64) β
- Example 4: Configuration pattern β
- Example 5: State machine pattern β
- Example 6: Test-driven pattern (assertions) β
Requires Advanced Features (3/9):
- Example 7: Accumulator pattern - needs arrays
[i32; 5],let mut - Example 8: Builder pattern - needs
String::new() - Example 9: Pattern composition - needs
String::from(),.to_string()
Next Steps: File feature requests for arrays, mut, String construction methods
The Problem
You know variables, functions, and control flow, but how do you combine them to solve real problems? Programming isnβt just about syntaxβitβs about recognizing patterns that appear repeatedly and expressing them clearly and efficiently.
Quick Example
// Calculator with validation and error handling
fun safe_calculate(operation: &str, a: i32, b: i32) -> i32 {
if operation == "add" {
a + b
} else if operation == "subtract" {
a - b
} else if operation == "multiply" {
a * b
} else if operation == "divide" {
if b == 0 {
println("Error: Division by zero");
0 // Safe default
} else {
a / b
}
} else {
println("Error: Unknown operation '{}'", operation);
0 // Safe default
}
}
fun main() {
let result1 = safe_calculate("add", 10, 5);
let result2 = safe_calculate("divide", 12, 3);
let result3 = safe_calculate("divide", 10, 0);
println("10 + 5 = {}", result1);
println("12 / 3 = {}", result2);
println("10 / 0 = {}", result3);
}
$ ruchy calculator.ruchy
10 + 5 = 15
12 / 3 = 4
Error: Division by zero
10 / 0 = 0
Core Concepts
Common Programming Patterns
- Input Validation: Check parameters before processing
- Guard Clauses: Handle edge cases early
- Default Values: Provide safe fallbacks
- Pattern Matching: Handle multiple cases systematically
- State Transformation: Transform data step by step
Why Patterns Matter
- Reliability: Consistent error handling
- Readability: Familiar structures
- Maintainability: Standard approaches
- Reusability: Templates for similar problems
Practical Usage
Validation and Guard Patterns
fun validate_user_input(name: &str, age: i32, email: &str) -> bool {
// Guard clause: check for empty name
if name.len() == 0 {
println("Error: Name cannot be empty");
return false;
}
// Guard clause: check age range
if age < 0 || age > 150 {
println("Error: Age must be between 0 and 150");
return false;
}
// Guard clause: basic email validation
if !email.contains('@') {
println("Error: Invalid email format");
return false;
}
// All validations passed
println("User input is valid");
return true;
}
fun create_user_profile(name: &str, age: i32, email: &str) -> &str {
if validate_user_input(name, age, email) {
println("Creating profile for: {}", name);
return "Profile created successfully";
} else {
return "Profile creation failed";
}
}
fun main() {
let result1 = create_user_profile("Alice", 25, "alice@example.com");
let result2 = create_user_profile("", 30, "bob@example.com");
let result3 = create_user_profile("Charlie", -5, "charlie@example.com");
println("Result 1: {}", result1);
println("Result 2: {}", result2);
println("Result 3: {}", result3);
}
Multi-Step Processing Patterns
fun process_score(raw_score: i32, max_score: i32) -> f64 {
// Step 1: Validate inputs
if max_score <= 0 {
println("Error: Max score must be positive");
return 0.0;
}
if raw_score < 0 {
println("Warning: Negative score adjusted to 0");
return 0.0;
}
if raw_score > max_score {
println("Warning: Score exceeds maximum, capping at {}", max_score);
return 100.0;
}
// Step 2: Calculate percentage
let percentage = (raw_score as f64) / (max_score as f64) * 100.0;
// Step 3: Round to reasonable precision
let rounded = (percentage * 10.0).round() / 10.0;
// Step 4: Return result
rounded
}
fun grade_assignment(student: &str, raw_score: i32, max_score: i32) -> &str {
let percentage = process_score(raw_score, max_score);
println("Student: {}", student);
println("Score: {}/{} ({:.1}%)", raw_score, max_score, percentage);
// Letter grade assignment
if percentage >= 90.0 {
return "A";
} else if percentage >= 80.0 {
return "B";
} else if percentage >= 70.0 {
return "C";
} else if percentage >= 60.0 {
return "D";
} else {
return "F";
}
}
fun main() {
let grade1 = grade_assignment("Alice", 95, 100);
let grade2 = grade_assignment("Bob", 42, 50);
let grade3 = grade_assignment("Charlie", 150, 100);
println("Grades: {}, {}, {}", grade1, grade2, grade3);
}
Configuration and Default Patterns
fun get_setting(setting_name: &str, default_value: i32) -> i32 {
// Simulate configuration lookup
if setting_name == "timeout" {
return 30;
} else if setting_name == "max_retries" {
return 3;
} else if setting_name == "buffer_size" {
return 1024;
} else {
println("Warning: Unknown setting '{}', using default {}", setting_name, default_value);
return default_value;
}
}
fun initialize_system() -> bool {
println("Initializing system...");
// Get settings with defaults
let timeout = get_setting("timeout", 15);
let retries = get_setting("max_retries", 1);
let buffer = get_setting("buffer_size", 512);
let unknown = get_setting("cache_size", 256);
// Display configuration
println("Configuration:");
println(" Timeout: {} seconds", timeout);
println(" Max retries: {}", retries);
println(" Buffer size: {} bytes", buffer);
println(" Cache size: {} MB", unknown);
// Validate critical settings
if timeout <= 0 {
println("Error: Timeout must be positive");
return false;
}
if retries < 0 {
println("Error: Retries cannot be negative");
return false;
}
println("β
System initialized successfully");
return true;
}
fun main() {
let success = initialize_system();
if success {
println("System is ready for operation");
} else {
println("System initialization failed");
}
}
Problem-Solving Templates
The Accumulator Pattern
fun calculate_total(prices: [i32; 5]) -> i32 {
let mut total = 0; // Accumulator starts at zero
let mut i = 0;
while i < 5 {
total = total + prices[i]; // Accumulate each value
i = i + 1;
}
total // Return accumulated result
}
fun find_maximum(numbers: [i32; 5]) -> i32 {
let mut max_value = numbers[0]; // Accumulator starts with first value
let mut i = 1;
while i < 5 {
if numbers[i] > max_value {
max_value = numbers[i]; // Update accumulator if better value found
}
i = i + 1;
}
max_value // Return best value found
}
fun count_positives(numbers: [i32; 5]) -> i32 {
let mut count = 0; // Counter accumulator
let mut i = 0;
while i < 5 {
if numbers[i] > 0 {
count = count + 1; // Increment counter for matches
}
i = i + 1;
}
count // Return count
}
fun main() {
let prices = [10, 25, 5, 15, 8];
let numbers = [-3, 7, -1, 12, 0];
let total = calculate_total(prices);
let maximum = find_maximum(prices);
let positive_count = count_positives(numbers);
println("Total: {}", total);
println("Maximum: {}", maximum);
println("Positive numbers: {}", positive_count);
}
The State Machine Pattern
fun process_order_state(current_state: &str, action: &str) -> &str {
if current_state == "pending" {
if action == "pay" {
println("Payment received, order confirmed");
return "confirmed";
} else if action == "cancel" {
println("Order cancelled");
return "cancelled";
} else {
println("Invalid action '{}' for pending order", action);
return current_state;
}
} else if current_state == "confirmed" {
if action == "ship" {
println("Order shipped");
return "shipped";
} else if action == "cancel" {
println("Confirmed order cancelled, refund processed");
return "cancelled";
} else {
println("Invalid action '{}' for confirmed order", action);
return current_state;
}
} else if current_state == "shipped" {
if action == "deliver" {
println("Order delivered");
return "delivered";
} else {
println("Cannot modify shipped order");
return current_state;
}
} else if current_state == "delivered" {
println("Order already completed");
return current_state;
} else if current_state == "cancelled" {
println("Order was cancelled");
return current_state;
} else {
println("Unknown order state: {}", current_state);
return "error";
}
}
fun track_order() -> &str {
let mut state = "pending";
println("Order tracking simulation:");
println("Initial state: {}", state);
// Process sequence of actions
state = process_order_state(state, "pay");
println("Current state: {}", state);
state = process_order_state(state, "ship");
println("Current state: {}", state);
state = process_order_state(state, "deliver");
println("Current state: {}", state);
state
}
fun main() {
let final_state = track_order();
println("Final order state: {}", final_state);
}
The Builder Pattern (Simple Version)
fun build_greeting(name: &str, formal: bool, include_time: bool) -> String {
let mut greeting = String::new();
// Start with appropriate formality
if formal {
greeting = greeting + "Good day, ";
} else {
greeting = greeting + "Hello, ";
}
// Add the name
greeting = greeting + name;
// Add time information if requested
if include_time {
greeting = greeting + "! Hope you're having a great day";
} else {
greeting = greeting + "!";
}
greeting
}
fun build_email_subject(priority: &str, department: &str, topic: &str) -> String {
let mut subject = String::new();
// Add priority prefix
if priority == "urgent" {
subject = subject + "[URGENT] ";
} else if priority == "high" {
subject = subject + "[HIGH] ";
}
// Add department prefix
subject = subject + "[" + department + "] ";
// Add main topic
subject = subject + topic;
subject
}
fun main() {
let greeting1 = build_greeting("Alice", true, true);
let greeting2 = build_greeting("Bob", false, false);
let subject1 = build_email_subject("urgent", "IT", "Server maintenance required");
let subject2 = build_email_subject("normal", "HR", "Team meeting scheduled");
println("Greetings:");
println(" {}", greeting1);
println(" {}", greeting2);
println("Email subjects:");
println(" {}", subject1);
println(" {}", subject2);
}
Pattern Composition
Combining Multiple Patterns
fun process_student_data(name: &str, scores: [i32; 3]) -> String {
// Pattern 1: Input validation
if name.len() == 0 {
return String::from("Error: Student name required");
}
// Pattern 2: Accumulator for total
let mut total = 0;
let mut i = 0;
while i < 3 {
if scores[i] < 0 || scores[i] > 100 {
return String::from("Error: Scores must be between 0 and 100");
}
total = total + scores[i];
i = i + 1;
}
// Pattern 3: Calculation with validation
let average = total / 3;
// Pattern 4: State-based classification
let grade = if average >= 90 {
"A"
} else if average >= 80 {
"B"
} else if average >= 70 {
"C"
} else if average >= 60 {
"D"
} else {
"F"
};
// Pattern 5: Builder pattern for result
let mut result = String::from("Student Report\n");
result = result + "Name: " + name + "\n";
result = result + "Scores: ";
// Add individual scores
let mut i = 0;
while i < 3 {
if i > 0 {
result = result + ", ";
}
result = result + &scores[i].to_string();
i = i + 1;
}
result = result + "\n";
result = result + "Average: " + &average.to_string() + "\n";
result = result + "Grade: " + grade;
result
}
fun main() {
let report1 = process_student_data("Alice Johnson", [95, 87, 92]);
let report2 = process_student_data("Bob Smith", [78, 82, 75]);
let report3 = process_student_data("", [85, 90, 88]);
println("{}\n", report1);
println("{}\n", report2);
println("{}\n", report3);
}
Testing Patterns
Test-Driven Pattern Development
// Test helper function
fun assert_equal(actual: i32, expected: i32, test_name: &str) {
if actual == expected {
println("β
{}: {} == {}", test_name, actual, expected);
} else {
println("β {}: {} != {} (expected)", test_name, actual, expected);
}
}
fun assert_string_equal(actual: &str, expected: &str, test_name: &str) {
if actual == expected {
println("β
{}: strings match", test_name);
} else {
println("β {}: '{}' != '{}' (expected)", test_name, actual, expected);
}
}
// Function to test
fun calculate_discount(price: i32, discount_percent: i32) -> i32 {
if discount_percent < 0 || discount_percent > 100 {
return price; // No discount for invalid percentage
}
let discount_amount = (price * discount_percent) / 100;
price - discount_amount
}
// Test suite
fun test_discount_calculation() {
println("Testing discount calculation...");
// Normal cases
assert_equal(calculate_discount(100, 10), 90, "10% discount on $100");
assert_equal(calculate_discount(50, 20), 40, "20% discount on $50");
assert_equal(calculate_discount(200, 0), 200, "0% discount on $200");
// Edge cases
assert_equal(calculate_discount(100, -5), 100, "Negative discount");
assert_equal(calculate_discount(100, 150), 100, "Over 100% discount");
assert_equal(calculate_discount(0, 50), 0, "50% discount on $0");
println("Discount tests completed.\n");
}
fun main() {
test_discount_calculation();
// Demo the actual function
println("Discount examples:");
println("$100 with 15% discount: ${}", calculate_discount(100, 15));
println("$250 with 25% discount: ${}", calculate_discount(250, 25));
}
Summary
- Validation patterns catch errors early and provide clear feedback
- Guard clauses simplify control flow and improve readability
- Accumulator patterns process collections systematically
- State machines model complex workflows reliably
- Builder patterns construct complex data step by step
- Pattern composition combines simple patterns for complex solutions
- Test-driven development ensures patterns work correctly
- Performance patterns optimize common operations
These patterns form the foundation of reliable, maintainable Ruchy programs. Master them, and youβll recognize solutions to most programming problems you encounter.