Skip to content

Commit 62922fb

Browse files
committed
add lazy variant
1 parent c2ff872 commit 62922fb

5 files changed

Lines changed: 410 additions & 15 deletions

File tree

README.md

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,11 +15,13 @@
1515
- `Owned` - Heap-allocated owned data via `Box<T>`
1616
- `Shared` - `Arc<T>` for shared immutable data
1717
- `Updatable` - Lock-free atomic updates using `arc-swap`
18+
- `Lazy` - Lazy initialization with atomic updates for static contexts
1819

19-
- **Lock-Free Updates**: The `Updatable` variant uses `arc-swap` for atomic, lock-free updates
20+
- **Lock-Free Updates**: The `Updatable` and `Lazy` variants use `arc-swap` for atomic, lock-free updates
21+
- **Lazy Initialization**: The `Lazy` variant makes it possbile to create an `Updatable` in a static context
2022
- **Flexible API**: Easy conversion between different storage types
2123
- **Zero-Cost Abstractions**: Minimal overhead for common operations
22-
- **Thread-Safe**: Share data safely across threads with `Shared` and `Updatable` variants
24+
- **Thread-Safe**: Share data safely across threads with `Shared`, `Updatable`, and `Lazy` variants
2325

2426
## 📦 Installation
2527

@@ -35,6 +37,7 @@ anycow = "0.1"
3537
AnyCow shines in scenarios where you have:
3638

3739
- **Configuration data** that's read frequently but updated occasionally
40+
- **Global/static data** that needs lazy initialization and atomic updates
3841
- **Cached values** that need atomic updates without locks
3942
- **Shared state** across multiple threads with infrequent modifications
4043
- **Hot paths** where you want to minimize allocation overhead
@@ -50,13 +53,15 @@ let borrowed = AnyCow::borrowed(&"hello");
5053
let owned = AnyCow::owned(String::from("world"));
5154
let shared = AnyCow::shared(std::sync::Arc::new(42));
5255
let updatable = AnyCow::updatable(vec![1, 2, 3]);
56+
let lazy = AnyCow::lazy(|| vec![7, 8, 9]);
5357

5458
// Read values efficiently
5559
println!("{}", *borrowed.borrow()); // "hello"
5660
println!("{}", *owned.borrow()); // "world"
5761

5862
// Atomic updates (lock-free!)
5963
updatable.try_replace(vec![4, 5, 6]).unwrap();
64+
lazy.try_replace(vec![10, 11, 12]).unwrap();
6065
```
6166

6267
## 💡 Examples
@@ -130,6 +135,37 @@ cache.try_replace(vec![4, 5, 6, 7, 8]).unwrap();
130135
reader.join().unwrap();
131136
```
132137

138+
### Lazy Global Configuration
139+
140+
```rust
141+
use anycow::AnyCow;
142+
use std::collections::HashMap;
143+
144+
// Perfect for static/global data that's expensive to initialize
145+
static CONFIG: AnyCow<HashMap<String, String>> = AnyCow::lazy(|| {
146+
println!("Loading configuration..."); // Only runs once!
147+
let mut config = HashMap::new();
148+
config.insert("app_name".to_string(), "MyApp".to_string());
149+
config.insert("version".to_string(), "1.0.0".to_string());
150+
config
151+
});
152+
153+
fn main() {
154+
// First access initializes the config
155+
let app_name = CONFIG.borrow().get("app_name").cloned().unwrap();
156+
println!("App: {}", app_name);
157+
158+
// Subsequent accesses are fast (no re-initialization)
159+
let version = CONFIG.borrow().get("version").cloned().unwrap();
160+
println!("Version: {}", version);
161+
162+
// Update the global config atomically
163+
let mut new_config = HashMap::new();
164+
new_config.insert("app_name".to_string(), "MyApp Pro".to_string());
165+
new_config.insert("version".to_string(), "2.0.0".to_string());
166+
CONFIG.try_replace(new_config).unwrap();
167+
}
168+
133169
## 🧠 Storage Strategy Guide
134170

135171
| Variant | Best For | Thread Safe | Mutable | Memory |
@@ -138,6 +174,7 @@ reader.join().unwrap();
138174
| `Owned` | Exclusive ownership ||| Heap |
139175
| `Shared` | Read-only sharing ||| Shared |
140176
| `Updatable` | Concurrent reads + atomic updates || Via `try_replace()` | Shared + Atomic |
177+
| `Lazy` | Static/global data + atomic updates || Via `try_replace()` | Lazy + Shared + Atomic |
141178

142179
## 🔧 API Reference
143180

@@ -147,6 +184,7 @@ AnyCow::borrowed(&value) // From reference
147184
AnyCow::owned(value) // From owned value (boxed)
148185
AnyCow::shared(arc) // From Arc<T>
149186
AnyCow::updatable(value) // Create updatable variant
187+
AnyCow::lazy(init_fn) // Create lazy variant with init function
150188
```
151189

152190
### Access
@@ -159,15 +197,16 @@ container.to_arc() // Convert to Arc<T>
159197

160198
### Updates
161199
```rust
162-
container.try_replace(new_value) // Atomic update (Updatable only)
200+
container.try_replace(new_value) // Atomic update (Updatable & Lazy only)
163201
```
164202

165203
## ⚡ Performance
166204

167205
AnyCow is designed for performance:
168206

169207
- **Zero-cost borrowing**: No allocation for `Borrowed` variant
170-
- **Lock-free updates**: `Updatable` uses `arc-swap` for atomic operations
208+
- **Lazy initialization**: `Lazy` variant allow to create an `Updatable` in a static context
209+
- **Lock-free updates**: `Updatable` and `Lazy` use `arc-swap` for atomic operations
171210
- **Minimal overhead**: Smart enum design with efficient memory layout
172211
- **Branch prediction friendly**: Common operations are optimized
173212

examples/const_functions.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use anycow::AnyCow;
2+
3+
// Lazy initialization works great in static contexts!
4+
static GLOBAL_CONFIG: AnyCow<Vec<i32>> = AnyCow::lazy(|| vec![1, 2, 3]);
5+
6+
// Borrowed can also be used in const contexts
7+
const BORROWED_STR: AnyCow<&str> = AnyCow::borrowed(&"hello world");
8+
9+
fn main() {
10+
println!("Const functions example:");
11+
12+
// Access the borrowed const
13+
println!("Borrowed const: {}", *BORROWED_STR.borrow());
14+
15+
// First access to lazy initializes it
16+
println!("Global config (first access): {:?}", *GLOBAL_CONFIG.borrow());
17+
18+
// Update the lazy value atomically
19+
GLOBAL_CONFIG.try_replace(vec![4, 5, 6, 7]).unwrap();
20+
println!("Global config (after update): {:?}", *GLOBAL_CONFIG.borrow());
21+
22+
// Another update
23+
GLOBAL_CONFIG.try_replace(vec![8, 9, 10]).unwrap();
24+
println!("Global config (final): {:?}", *GLOBAL_CONFIG.borrow());
25+
}

examples/lazy_demo.rs

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
use anycow::AnyCow;
2+
use std::collections::HashMap;
3+
use std::sync::atomic::{AtomicUsize, Ordering};
4+
5+
// Global configuration that's lazily initialized
6+
static CONFIG: AnyCow<HashMap<String, String>> = AnyCow::lazy(|| {
7+
println!("Initializing global config...");
8+
let mut config = HashMap::new();
9+
config.insert("app_name".to_string(), "MyApp".to_string());
10+
config.insert("version".to_string(), "1.0.0".to_string());
11+
config.insert("debug".to_string(), "false".to_string());
12+
config
13+
});
14+
15+
// A counter to track how many times expensive computation runs
16+
static COMPUTATION_COUNTER: AtomicUsize = AtomicUsize::new(0);
17+
18+
// Expensive computation that we want to lazy-load and cache
19+
static EXPENSIVE_RESULT: AnyCow<Vec<u64>> = AnyCow::lazy(|| {
20+
let count = COMPUTATION_COUNTER.fetch_add(1, Ordering::SeqCst);
21+
println!("Running expensive computation #{}", count + 1);
22+
23+
// Simulate expensive computation
24+
(1..=10).map(|i| i * i * i).collect()
25+
});
26+
27+
fn main() {
28+
println!("=== AnyCow Lazy Example ===\n");
29+
30+
// 1. Demonstrate that lazy initialization doesn't happen until first access
31+
println!("1. Created lazy statics, but nothing initialized yet");
32+
println!(" Computation counter: {}\n", COMPUTATION_COUNTER.load(Ordering::SeqCst));
33+
34+
// 2. First access to CONFIG initializes it
35+
println!("2. First access to CONFIG:");
36+
let app_name = CONFIG.borrow().get("app_name").cloned().unwrap_or_default();
37+
println!(" App name: {}", app_name);
38+
println!(" Computation counter: {}\n", COMPUTATION_COUNTER.load(Ordering::SeqCst));
39+
40+
// 3. Subsequent accesses don't re-initialize
41+
println!("3. Second access to CONFIG (no re-initialization):");
42+
let version = CONFIG.borrow().get("version").cloned().unwrap_or_default();
43+
println!(" Version: {}", version);
44+
println!(" Computation counter: {}\n", COMPUTATION_COUNTER.load(Ordering::SeqCst));
45+
46+
// 4. Update the config atomically
47+
println!("4. Updating CONFIG atomically:");
48+
let mut new_config = HashMap::new();
49+
new_config.insert("app_name".to_string(), "MyApp Pro".to_string());
50+
new_config.insert("version".to_string(), "2.0.0".to_string());
51+
new_config.insert("debug".to_string(), "true".to_string());
52+
new_config.insert("theme".to_string(), "dark".to_string());
53+
54+
CONFIG.try_replace(new_config).unwrap();
55+
56+
let updated_app_name = CONFIG.borrow().get("app_name").cloned().unwrap_or_default();
57+
let theme = CONFIG.borrow().get("theme").cloned().unwrap_or_default();
58+
println!(" Updated app name: {}", updated_app_name);
59+
println!(" New theme: {}", theme);
60+
println!(" Computation counter: {}\n", COMPUTATION_COUNTER.load(Ordering::SeqCst));
61+
62+
// 5. Now access the expensive computation for the first time
63+
println!("5. First access to EXPENSIVE_RESULT (will initialize):");
64+
let result = EXPENSIVE_RESULT.borrow();
65+
println!(" Cubes: {:?}", *result);
66+
println!(" Computation counter: {}\n", COMPUTATION_COUNTER.load(Ordering::SeqCst));
67+
68+
// 6. Access it again (no re-computation)
69+
println!("6. Second access to EXPENSIVE_RESULT (no re-computation):");
70+
let result2 = EXPENSIVE_RESULT.borrow();
71+
println!(" Cubes again: {:?}", *result2);
72+
println!(" Computation counter: {}\n", COMPUTATION_COUNTER.load(Ordering::SeqCst));
73+
74+
// 7. Update the expensive result
75+
println!("7. Updating EXPENSIVE_RESULT:");
76+
let new_result: Vec<u64> = (1..=5).map(|i| i * i).collect();
77+
EXPENSIVE_RESULT.try_replace(new_result).unwrap();
78+
79+
let updated_result = EXPENSIVE_RESULT.borrow();
80+
println!(" Updated to squares: {:?}", *updated_result);
81+
println!(" Computation counter: {}\n", COMPUTATION_COUNTER.load(Ordering::SeqCst));
82+
83+
// 8. Demonstrate local lazy usage
84+
println!("8. Local lazy usage:");
85+
let local_lazy = AnyCow::lazy(|| {
86+
println!(" Initializing local lazy value...");
87+
String::from("Hello from local lazy!")
88+
});
89+
90+
println!(" Created local lazy (not initialized yet)");
91+
println!(" Value: {}", *local_lazy.borrow());
92+
println!(" Second access: {}", *local_lazy.borrow());
93+
94+
println!("\n=== Summary ===");
95+
println!("- Lazy initialization only happens on first access");
96+
println!("- Subsequent accesses are fast (no re-initialization)");
97+
println!("- Atomic updates work just like with Updatable variant");
98+
println!("- Perfect for static/const contexts");
99+
println!("- Thread-safe and lock-free");
100+
}

0 commit comments

Comments
 (0)