lazy_evaluation/
lazy_evaluation.rs

1// Demonstrates lazy query evaluation using iterators
2// Shows how lazy evaluation defers work until results are needed
3// and enables early termination for better performance
4// cargo run --example lazy_evaluation
5
6use rust_queries_builder::LazyQuery;
7use key_paths_derive::Keypaths;
8use std::sync::atomic::{AtomicUsize, Ordering};
9
10// Counter to track how many times predicates are evaluated
11static FILTER_EVALUATIONS: AtomicUsize = AtomicUsize::new(0);
12static EXPENSIVE_OPERATIONS: AtomicUsize = AtomicUsize::new(0);
13
14#[derive(Debug, Clone, Keypaths)]
15struct Product {
16    id: u32,
17    name: String,
18    price: f64,
19    category: String,
20    stock: u32,
21}
22
23fn create_products() -> Vec<Product> {
24    (1..=1000)
25        .map(|i| Product {
26            id: i,
27            name: format!("Product {}", i),
28            price: (i as f64 * 10.0) % 1000.0,
29            category: if i % 3 == 0 {
30                "Electronics".to_string()
31            } else if i % 3 == 1 {
32                "Furniture".to_string()
33            } else {
34                "Clothing".to_string()
35            },
36            stock: i % 50,
37        })
38        .collect()
39}
40
41fn expensive_check(value: &f64) -> bool {
42    EXPENSIVE_OPERATIONS.fetch_add(1, Ordering::SeqCst);
43    // Simulate expensive operation
44    value > &100.0
45}
46
47fn main() {
48    println!("\n╔════════════════════════════════════════════════════════════════╗");
49    println!("║  Lazy Query Evaluation Demo                                   ║");
50    println!("╚════════════════════════════════════════════════════════════════╝\n");
51
52    let products = create_products();
53    println!("Created {} products\n", products.len());
54
55    // ============================================================================
56    // DEMO 1: Lazy Execution - Nothing Happens Until .collect()
57    // ============================================================================
58    println!("═══════════════════════════════════════════════════════════════");
59    println!("Demo 1: Lazy execution - deferred until needed");
60    println!("═══════════════════════════════════════════════════════════════\n");
61
62    FILTER_EVALUATIONS.store(0, Ordering::SeqCst);
63
64    println!("Building query (should execute nothing)...");
65    let lazy_query = LazyQuery::new(&products)
66        .where_(Product::category_r(), |cat| {
67            FILTER_EVALUATIONS.fetch_add(1, Ordering::SeqCst);
68            cat == "Electronics"
69        });
70
71    let evals_after_build = FILTER_EVALUATIONS.load(Ordering::SeqCst);
72    println!("  Filter evaluations after building query: {}", evals_after_build);
73    
74    if evals_after_build == 0 {
75        println!("  ✅ Confirmed: Query is lazy! Nothing executed yet.\n");
76    }
77
78    println!("Collecting results (now it executes)...");
79    let results: Vec<_> = lazy_query.collect();
80
81    let evals_after_collect = FILTER_EVALUATIONS.load(Ordering::SeqCst);
82    println!("  Filter evaluations after collecting: {}", evals_after_collect);
83    println!("  Results found: {}", results.len());
84    println!("  ✅ Query executed exactly once, when needed!\n");
85
86    // ============================================================================
87    // DEMO 2: Early Termination - Stops as Soon as Enough Items Found
88    // ============================================================================
89    println!("═══════════════════════════════════════════════════════════════");
90    println!("Demo 2: Early termination with .take()");
91    println!("═══════════════════════════════════════════════════════════════\n");
92
93    EXPENSIVE_OPERATIONS.store(0, Ordering::SeqCst);
94
95    println!("Finding first 5 expensive items from 1000 products...");
96    let first_5: Vec<_> = LazyQuery::new(&products)
97        .where_(Product::price_r(), |p| expensive_check(p))
98        .take_lazy(5)
99        .collect();
100
101    let ops = EXPENSIVE_OPERATIONS.load(Ordering::SeqCst);
102    println!("  Found: {} items", first_5.len());
103    println!("  Expensive operations performed: {}", ops);
104    println!("  Items NOT checked: {} (stopped early!)", 1000 - ops);
105    
106    if ops < 1000 {
107        println!("  ✅ Early termination worked! Didn't check all 1000 items.\n");
108    }
109
110    // ============================================================================
111    // DEMO 3: Iterator Fusion - Rust Optimizes Chained Operations
112    // ============================================================================
113    println!("═══════════════════════════════════════════════════════════════");
114    println!("Demo 3: Iterator fusion - chained operations optimized");
115    println!("═══════════════════════════════════════════════════════════════\n");
116
117    println!("Chaining multiple operations...");
118    let chained_query = LazyQuery::new(&products)
119        .where_(Product::category_r(), |cat| cat == "Electronics")
120        .where_(Product::price_r(), |&price| price > 200.0)
121        .where_(Product::stock_r(), |&stock| stock > 10)
122        .take_lazy(10);
123
124    println!("  Built query with 3 filters + take(10)");
125    println!("  ✅ No execution yet - all operations fused into one iterator\n");
126
127    let results: Vec<_> = chained_query.collect();
128    println!("  Executed: Found {} items", results.len());
129    println!("  ✅ All filters applied in single pass!\n");
130
131    // ============================================================================
132    // DEMO 4: Lazy Projection - Only Extract What You Need
133    // ============================================================================
134    println!("═══════════════════════════════════════════════════════════════");
135    println!("Demo 4: Lazy projection");
136    println!("═══════════════════════════════════════════════════════════════\n");
137
138    println!("Selecting names (lazy)...");
139    let names: Vec<String> = LazyQuery::new(&products)
140        .where_(Product::category_r(), |cat| cat == "Electronics")
141        .select_lazy(Product::name_r())
142        .take(5)  // Only process until we have 5
143        .collect();
144
145    println!("  Selected {} names", names.len());
146    println!("  ✅ Only evaluated until 5 names found!\n");
147
148    // ============================================================================
149    // DEMO 5: Lazy Aggregation - Short-Circuit When Possible
150    // ============================================================================
151    println!("═══════════════════════════════════════════════════════════════");
152    println!("Demo 5: Short-circuit with .any()");
153    println!("═══════════════════════════════════════════════════════════════\n");
154
155    FILTER_EVALUATIONS.store(0, Ordering::SeqCst);
156
157    println!("Checking if ANY electronics exist (1000 items to search)...");
158    let exists = LazyQuery::new(&products)
159        .where_(Product::category_r(), |cat| {
160            FILTER_EVALUATIONS.fetch_add(1, Ordering::SeqCst);
161            cat == "Electronics"
162        })
163        .any();
164
165    let checks = FILTER_EVALUATIONS.load(Ordering::SeqCst);
166    println!("  Result: {}", exists);
167    println!("  Items checked: {} out of 1000", checks);
168    println!("  Items skipped: {} (short-circuited!)", 1000 - checks);
169    
170    if checks < 1000 {
171        println!("  ✅ Short-circuit worked! Stopped as soon as first match found.\n");
172    }
173
174    // ============================================================================
175    // DEMO 6: Lazy Find - Stops at First Match
176    // ============================================================================
177    println!("═══════════════════════════════════════════════════════════════");
178    println!("Demo 6: .find() stops at first match");
179    println!("═══════════════════════════════════════════════════════════════\n");
180
181    FILTER_EVALUATIONS.store(0, Ordering::SeqCst);
182
183    println!("Finding first product with price > 500...");
184    let found = LazyQuery::new(&products)
185        .where_(Product::price_r(), |&price| {
186            FILTER_EVALUATIONS.fetch_add(1, Ordering::SeqCst);
187            price > 500.0
188        })
189        .first();
190
191    let checks = FILTER_EVALUATIONS.load(Ordering::SeqCst);
192    if let Some(product) = found {
193        println!("  Found: {} (${:.2})", product.name, product.price);
194    }
195    println!("  Items checked: {} out of 1000", checks);
196    println!("  ✅ Stopped immediately after finding first match!\n");
197
198    // ============================================================================
199    // DEMO 7: Composition - Build Queries Incrementally
200    // ============================================================================
201    println!("═══════════════════════════════════════════════════════════════");
202    println!("Demo 7: Composable queries");
203    println!("═══════════════════════════════════════════════════════════════\n");
204
205    println!("Building base query...");
206    let base_query = LazyQuery::new(&products)
207        .where_(Product::category_r(), |cat| cat == "Electronics");
208
209    println!("  Created base query (not executed)\n");
210
211    println!("  Adding price filter...");
212    let refined_query = base_query
213        .where_(Product::price_r(), |&price| price > 100.0);
214
215    println!("  Still not executed...\n");
216
217    println!("  Adding stock filter and limiting...");
218    let final_query = refined_query
219        .where_(Product::stock_r(), |&stock| stock > 5)
220        .take_lazy(10);
221
222    println!("  Still not executed...\n");
223
224    println!("  Executing...");
225    let results: Vec<_> = final_query.collect();
226    println!("  ✅ Executed once with all filters: Found {} items\n", results.len());
227
228    // ============================================================================
229    // DEMO 8: For Loop - Natural Iteration
230    // ============================================================================
231    println!("═══════════════════════════════════════════════════════════════");
232    println!("Demo 8: Use in for loops");
233    println!("═══════════════════════════════════════════════════════════════\n");
234
235    println!("Iterating over filtered products...");
236    let mut count = 0;
237    for product in LazyQuery::new(&products)
238        .where_(Product::category_r(), |cat| cat == "Electronics")
239        .take_lazy(3)
240    {
241        println!("  • {}: ${:.2}", product.name, product.price);
242        count += 1;
243    }
244    println!("  ✅ Processed {} items lazily\n", count);
245
246    // ============================================================================
247    // DEMO 9: Performance Comparison
248    // ============================================================================
249    println!("═══════════════════════════════════════════════════════════════");
250    println!("Demo 9: Performance benefit demonstration");
251    println!("═══════════════════════════════════════════════════════════════\n");
252
253    println!("Scenario: Find first expensive item from 1000 products\n");
254
255    EXPENSIVE_OPERATIONS.store(0, Ordering::SeqCst);
256    
257    println!("Without lazy (hypothetical - check all, then take first):");
258    println!("  Would check: 1000 items");
259    println!("  Would find: ~300 matching items");
260    println!("  Would return: 1 item");
261    println!("  Wasted work: 299 items processed unnecessarily\n");
262
263    EXPENSIVE_OPERATIONS.store(0, Ordering::SeqCst);
264    
265    println!("With lazy evaluation:");
266    let _first = LazyQuery::new(&products)
267        .where_(Product::price_r(), |p| expensive_check(p))
268        .first();
269
270    let ops = EXPENSIVE_OPERATIONS.load(Ordering::SeqCst);
271    println!("  Checked: {} items", ops);
272    println!("  Found: 1 item");
273    println!("  Wasted work: 0 items");
274    println!("  ✅ Efficiency gain: {}x faster!", 1000 / ops.max(1));
275
276    // ============================================================================
277    // Summary
278    // ============================================================================
279    println!("\n╔════════════════════════════════════════════════════════════════╗");
280    println!("║  Lazy Evaluation Benefits                                     ║");
281    println!("╚════════════════════════════════════════════════════════════════╝\n");
282
283    println!("✅ Deferred Execution:");
284    println!("   • No work until results needed");
285    println!("   • Can build complex queries without performance cost\n");
286
287    println!("✅ Early Termination:");
288    println!("   • .take(n) stops after n items");
289    println!("   • .first() stops after 1 item");
290    println!("   • .any() stops after first match");
291    println!("   • Massive performance win for large datasets\n");
292
293    println!("✅ Iterator Fusion:");
294    println!("   • Multiple filters combined into one pass");
295    println!("   • Rust compiler optimizes chained operations");
296    println!("   • No intermediate allocations\n");
297
298    println!("✅ Composable:");
299    println!("   • Build queries incrementally");
300    println!("   • Reuse query fragments");
301    println!("   • Clean separation of query building vs execution\n");
302
303    println!("✅ Zero Overhead:");
304    println!("   • Compiles to same code as manual loops");
305    println!("   • No runtime cost for abstraction");
306    println!("   • Pay only for what you use\n");
307
308    println!("✓ Lazy evaluation demo complete!\n");
309}
310