rust 41 lines · 6 steps

A rolling average over a sliding window

A fixed-size ring keeps a running sum so each new reading yields an average in constant time.

Explained by highlit
1use std::collections::VecDeque;
2 
3pub struct RollingAverage {
4 window: VecDeque<f64>,
5 capacity: usize,
6 sum: f64,
7}
8 
9impl RollingAverage {
10 pub fn new(capacity: usize) -> Self {
11 assert!(capacity > 0, "window capacity must be non-zero");
12 Self {
13 window: VecDeque::with_capacity(capacity),
14 capacity,
15 sum: 0.0,
16 }
17 }
18 
19 pub fn push(&mut self, reading: f64) -> f64 {
20 if self.window.len() == self.capacity {
21 if let Some(oldest) = self.window.pop_front() {
22 self.sum -= oldest;
23 }
24 }
25 self.window.push_back(reading);
26 self.sum += reading;
27 self.average()
28 }
29 
30 pub fn average(&self) -> f64 {
31 if self.window.is_empty() {
32 0.0
33 } else {
34 self.sum / self.window.len() as f64
35 }
36 }
37 
38 pub fn is_full(&self) -> bool {
39 self.window.len() == self.capacity
40 }
41}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Maintaining a running sum alongside the window turns each average into an O(1) update instead of re-summing the whole buffer.
  2. 2A VecDeque is the natural fit for a sliding window because it supports cheap pushes and pops at both ends.
  3. 3Keeping the sum in sync means every mutation must both add the new value and subtract whatever leaves the window.

Related explainers

Share this explainer

Here's the card — post it anywhere.

A rolling average over a sliding window — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code