bufferbuffer/
lib.rs

1use std::cell::{Ref, RefCell, RefMut};
2
3
4pub struct DoubleBuffer<T> {
5    first: RefCell<T>,
6    second: RefCell<T>,
7    switched: bool,
8}
9
10/// An implementation of the Double Buffer pattern from 'Game Programming Patterns' by Robert Nystrom.
11/// 
12/// In a simulation, you often have to do a lot of processing to prepare the next "frame", but if you're
13/// iterating through the current-state data while mutating it, things can slip.  The Double Buffer design
14/// pattern solves this by keeping two copies of the simulation state (or any variable): the "current"
15/// (or previous) state which is immutable, and the "next" (or future) state which is being prepared.  When
16/// a turn of the simulation is completed, you simply switch the buffers.
17/// 
18/// Unlike other implementations on crates.io, this one wraps both buffers in `std::cell::RefCell` so that
19/// it is possible to borrow one buffer as mutable at the same time the other is borrowed as immutable --
20/// a typical use case is to iterate over objects in the current state and write updated versions of them
21/// to the next state.
22impl<T> DoubleBuffer<T> {
23
24    pub fn new(current: T, next: T) -> Self {
25        Self {
26            first: RefCell::new(current),
27            second: RefCell::new(next),
28            switched: false,
29        }
30    }
31
32    /// Get an immutable reference to the current-state buffer.
33    pub fn current(&self) -> Ref<T> {
34        match self.switched {
35            false => self.first.borrow(),
36            true => self.second.borrow(),
37        }
38    }
39
40    /// Get a mutable reference to the next-state buffer.
41    pub fn next(&self) -> RefMut<T> {
42        match self.switched {
43            false => self.second.borrow_mut(),
44            true => self.first.borrow_mut(),
45        }
46    }
47
48    /// Get an immutable reference to the next-state buffer.
49    pub fn next_immut(&self) -> Ref<T> {
50        match self.switched {
51            false => self.second.borrow(),
52            true => self.first.borrow(),
53        }
54    }
55
56    /// Switch the "current" and "next" buffers.
57    pub fn switch(&mut self) {
58        self.switched = !self.switched;
59    }
60
61}
62
63
64#[cfg(test)]
65mod tests {
66    use crate::DoubleBuffer;
67
68
69    #[test]
70    fn switching_buffers_works() {
71        let mut my_double_buf: DoubleBuffer<i32> = DoubleBuffer::new(0,0);
72        *my_double_buf.next() += 10;
73        assert_eq!(*my_double_buf.current(), 0);
74        my_double_buf.switch();
75        assert_eq!(*my_double_buf.current(), 10);
76        my_double_buf.switch();
77        assert_eq!(*my_double_buf.current(), 0);
78    }
79
80
81    #[test]
82    fn writing_from_current_to_next_works() {
83        let mut my_double_buf: DoubleBuffer<Vec<i32>> = DoubleBuffer::new( vec!(2,4,6), Vec::new());
84        for number in my_double_buf.current().iter() {
85            my_double_buf.next().push(*number + 1);
86        }
87        my_double_buf.switch();
88        assert_eq!(*my_double_buf.current(), vec!(3,5,7));
89        *my_double_buf.next() = Vec::new();
90        for number in my_double_buf.current().iter() {
91            my_double_buf.next().push(*number + 1);
92        }
93        my_double_buf.switch();
94        assert_eq!(*my_double_buf.current(), vec!(4,6,8));
95
96     }
97
98
99}