Examples
Real-world usage patterns for ring-buffer-macro. Each example is a complete, working program.
Logging Buffer
A fixed-size log that drops oldest entries when full. Useful for embedded systems or any context where you want bounded memory usage for logs.
1use ring_buffer_macro::ring_buffer;2
3#[ring_buffer(1024)]4struct LogBuffer(String);5
6fn main() {7 let mut logs = LogBuffer::new();8
9 for i in 0..2000 {10 // Drop oldest entry if full11 if logs.is_full() {12 logs.dequeue();13 }14 logs.enqueue(format!("[{}] Event occurred", i)).unwrap();15 }16
17 // Only the last 1024 logs remain18 println!("Log count: {}", logs.len());19
20 for log in logs.iter() {21 println!("{}", log);22 }23}Audio Sample Buffer
Ring buffers are the standard data structure for real-time audio processing. The SPSC mode is ideal: one thread captures audio, another processes it.
1use ring_buffer_macro::ring_buffer;2use std::sync::Arc;3use std::thread;4
5#[ring_buffer(capacity = 4096, mode = "spsc")]6struct AudioBuffer(f32);7
8fn main() {9 let buf = Arc::new(AudioBuffer::new());10 let (producer, consumer) = buf.split();11
12 // Audio input thread13 let input = thread::spawn(move || {14 for i in 0..44100 {15 let sample = (i as f32 * 0.01).sin();16 while producer.try_enqueue(sample).is_err() {17 std::hint::spin_loop();18 }19 }20 });21
22 // Audio output thread23 let output = thread::spawn(move || {24 let mut processed = 0;25 while processed < 44100 {26 if let Some(sample) = consumer.try_dequeue() {27 // Apply gain, filter, etc.28 let _output = sample * 0.8;29 processed += 1;30 }31 }32 });33
34 input.join().unwrap();35 output.join().unwrap();36}Rate Limiter
Track timestamps of recent requests using a ring buffer as a sliding window. When the window is full, reject new requests until old ones expire.
1use ring_buffer_macro::ring_buffer;2use std::time::{Duration, Instant};3
4#[ring_buffer(100)]5struct RequestLog(Instant);6
7struct RateLimiter {8 log: RequestLog,9 window: Duration,10}11
12impl RateLimiter {13 fn new(max_requests: usize, window: Duration) -> Self {14 // Note: max_requests should match the buffer capacity15 Self {16 log: RequestLog::new(),17 window,18 }19 }20
21 fn allow(&mut self) -> bool {22 let now = Instant::now();23
24 // Remove expired entries from the front25 while let Some(&oldest) = self.log.peek() {26 if now.duration_since(oldest) > self.window {27 self.log.dequeue();28 } else {29 break;30 }31 }32
33 // Check if we have capacity34 if self.log.is_full() {35 return false;36 }37
38 self.log.enqueue(now).unwrap();39 true40 }41}42
43fn main() {44 let mut limiter = RateLimiter::new(100, Duration::from_secs(60));45
46 for _ in 0..150 {47 if limiter.allow() {48 println!("Request allowed");49 } else {50 println!("Rate limited");51 }52 }53}Multi-Producer Event System
Use MPSC mode to collect events from multiple producer threads into a single consumer that processes them sequentially.
1use ring_buffer_macro::ring_buffer;2use std::sync::Arc;3use std::thread;4
5#[ring_buffer(capacity = 256, mode = "mpsc")]6struct EventBus(String);7
8fn main() {9 let bus = Arc::new(EventBus::new());10 let consumer = bus.consumer();11
12 // Spawn multiple event producers13 let handles: Vec<_> = (0..4).map(|id| {14 let producer = bus.producer();15 thread::spawn(move || {16 for i in 0..10 {17 let event = format!("producer-{}: event-{}", id, i);18 while producer.try_enqueue(event.clone()).is_err() {19 std::hint::spin_loop();20 }21 }22 })23 }).collect();24
25 // Single consumer processes all events26 let mut count = 0;27 while count < 40 {28 if let Some(event) = consumer.try_dequeue() {29 println!("{}", event);30 count += 1;31 }32 }33
34 for h in handles {35 h.join().unwrap();36 }37}Moving Average Calculator
Compute a running average over the last N values. The ring buffer naturally maintains a fixed window of samples.
1use ring_buffer_macro::ring_buffer;2
3#[ring_buffer(10)]4struct SampleWindow(f64);5
6struct MovingAverage {7 window: SampleWindow,8}9
10impl MovingAverage {11 fn new() -> Self {12 Self { window: SampleWindow::new() }13 }14
15 fn push(&mut self, value: f64) {16 if self.window.is_full() {17 self.window.dequeue();18 }19 self.window.enqueue(value).unwrap();20 }21
22 fn average(&self) -> Option<f64> {23 if self.window.is_empty() {24 return None;25 }26 let sum: f64 = self.window.iter().sum();27 Some(sum / self.window.len() as f64)28 }29}30
31fn main() {32 let mut avg = MovingAverage::new();33
34 let readings = [23.1, 23.4, 23.2, 24.0, 23.8, 23.5,35 23.9, 24.1, 23.7, 23.6, 24.2, 24.5];36
37 for reading in readings {38 avg.push(reading);39 println!("Reading: {:.1}, Average: {:.2}",40 reading, avg.average().unwrap());41 }42}