API Reference

Complete reference for all methods generated by ring-buffer-macro, organized by mode.

Standard Mode

The default mode. Single-threaded, uses &mut self for mutations. Requires T: Clone for dequeue and drain.


new

fn new() -> Self

Creates a new empty buffer with the compile-time capacity. Head, tail, and size are initialized to 0.

let mut buf = Buffer::new();
assert!(buf.is_empty());
assert_eq!(buf.capacity(), 8); // from #[ring_buffer(8)]

enqueue

fn enqueue(&mut self, item: T) -> Result<(), T>

Adds an item to the back of the buffer. Returns Err(item) if the buffer is full, allowing you to recover the rejected value.

buf.enqueue(42).unwrap();
// Handle full buffer
if let Err(rejected) = buf.enqueue(99) {
println!("Buffer full, {} rejected", rejected);
}

dequeue

fn dequeue(&mut self) -> Option<T> where T: Clone

Removes and returns the oldest item from the front. Returns None if the buffer is empty. Requires T: Clone because it copies the value out of the internal Vec.

buf.enqueue(1).unwrap();
buf.enqueue(2).unwrap();
assert_eq!(buf.dequeue(), Some(1)); // FIFO order
assert_eq!(buf.dequeue(), Some(2));
assert_eq!(buf.dequeue(), None); // empty

peek / peek_mut / peek_back

fn peek(&self) -> Option<&T> fn peek_mut(&mut self) -> Option<&mut T> fn peek_back(&self) -> Option<&T>

View items without removing them. peek() returns the front (oldest), peek_back() returns the back (newest), and peek_mut() gives a mutable reference to the front.

buf.enqueue(1).unwrap();
buf.enqueue(2).unwrap();
buf.enqueue(3).unwrap();
assert_eq!(buf.peek(), Some(&1)); // oldest
assert_eq!(buf.peek_back(), Some(&3)); // newest
// Modify the front item
if let Some(front) = buf.peek_mut() {
*front = 10;
}
assert_eq!(buf.dequeue(), Some(10));

is_full / is_empty

fn is_full(&self) -> bool fn is_empty(&self) -> bool

Check buffer state. is_full() returns true when len equals capacity.

let mut buf = Buffer::new(); // capacity 4
assert!(buf.is_empty());
assert!(!buf.is_full());
for i in 0..4 { buf.enqueue(i).unwrap(); }
assert!(buf.is_full());
assert!(!buf.is_empty());

len / capacity

fn len(&self) -> usize fn capacity(&self) -> usize

len() returns the current number of items. capacity() returns the compile-time maximum.

let mut buf = Buffer::new(); // #[ring_buffer(8)]
assert_eq!(buf.len(), 0);
assert_eq!(buf.capacity(), 8);
buf.enqueue(1).unwrap();
assert_eq!(buf.len(), 1);

clear

fn clear(&mut self)

Removes all items from the buffer. Resets head, tail, and size to 0. Does not deallocate the underlying Vec.

buf.enqueue(1).unwrap();
buf.enqueue(2).unwrap();
buf.clear();
assert!(buf.is_empty());
assert_eq!(buf.len(), 0);

iter / drain

fn iter(&self) -> impl Iterator<Item = &T> fn drain(&mut self) -> BufferDrain<T> where T: Clone

iter() provides a non-consuming iterator over buffer contents in FIFO order. drain() returns a consuming iterator that removes items as it goes. The drain iterator cleans up remaining items on drop.

buf.enqueue(1).unwrap();
buf.enqueue(2).unwrap();
buf.enqueue(3).unwrap();
// Non-consuming iteration
for item in buf.iter() {
println!("{}", item);
}
assert_eq!(buf.len(), 3); // unchanged
// Consuming iteration
let items: Vec<_> = buf.drain().collect();
assert_eq!(items, vec![1, 2, 3]);
assert!(buf.is_empty());

SPSC Mode

Lock-free single-producer single-consumer mode. Uses atomic operations with Acquire/Release memory ordering. Requires T: Send.


new / split

fn new() -> Self fn split(&self) -> (SpscBufferProducer<'_>, SpscBufferConsumer<'_>)

Create the buffer with new(), then split() into producer and consumer handles. The split enforces the SPSC contract at the type level.

use std::sync::Arc;
#[ring_buffer(capacity = 256, mode = "spsc")]
struct SpscBuffer(i32);
let buf = Arc::new(SpscBuffer::new());
let (producer, consumer) = buf.split();

try_enqueue

fn try_enqueue(&self, item: T) -> Result<(), T>

Non-blocking enqueue. Returns Err(item) if the buffer is full. Uses Acquire load of head and Release store of tail.

match producer.try_enqueue(42) {
Ok(()) => println!("sent"),
Err(item) => println!("full, {} rejected", item),
}

try_dequeue / peek

fn try_dequeue(&self) -> Option<T> fn peek(&self) -> Option<&T>

Non-blocking dequeue. Returns None if empty. Uses Acquire load of tail and Release store of head. peek() views the front item without consuming it.

if let Some(item) = consumer.try_dequeue() {
println!("received: {}", item);
}
if let Some(front) = consumer.peek() {
println!("next: {}", front);
}

Blocking Operations

fn enqueue_blocking(&self, item: T) // Producer fn dequeue_blocking(&self) -> T // Consumer

Available when blocking = true. Uses Condvar to sleep instead of spinning. The data path remains lock-free — Mutex is only used for Condvar signaling.

#[ring_buffer(capacity = 64, mode = "spsc", blocking = true)]
struct BlockingSpsc(i32);
let buf = Arc::new(BlockingSpsc::new());
let (producer, consumer) = buf.split();
// These block instead of returning Err/None
producer.enqueue_blocking(42);
let item = consumer.dequeue_blocking();

MPSC Mode

Multiple producers, single consumer. Producers coordinate via CAS (Compare-And-Swap) on the tail index. Per-slot written flags ensure the consumer only reads completed writes. Requires T: Send.


new / producer / consumer

fn new() -> Self fn producer(&self) -> MpscBufferProducer<'_> // Clonable fn consumer(&self) -> MpscBufferConsumer<'_>

Create the buffer, then obtain producer and consumer handles. Producer handles are Clone — create one per producer thread.

#[ring_buffer(capacity = 128, mode = "mpsc")]
struct MpscBuffer(i32);
let buf = Arc::new(MpscBuffer::new());
let consumer = buf.consumer();
// Clone producers for each thread
let p1 = buf.producer();
let p2 = buf.producer();

try_enqueue

fn try_enqueue(&self, item: T) -> Result<(), T>

CAS-based non-blocking enqueue. Each producer atomically claims a slot via compare_exchange_weak on the tail, writes the item, then sets the written flag.

// Safe to call from multiple threads simultaneously
while producer.try_enqueue(value).is_err() {
std::hint::spin_loop();
}

try_dequeue / peek

fn try_dequeue(&self) -> Option<T> fn peek(&self) -> Option<&T>

Non-blocking dequeue. Checks the written flag before reading to ensure the producer has finished writing. peek() views the front item without consuming.

if let Some(item) = consumer.try_dequeue() {
process(item);
}
The consumer handle is not enforced as single-threaded at the type level. Creating multiple consumers is a logic error that may cause data races.

Blocking Operations

fn enqueue_blocking(&self, item: T) // Producer fn dequeue_blocking(&self) -> T // Consumer

Available when blocking = true. Same Condvar-based approach as SPSC blocking.

#[ring_buffer(capacity = 64, mode = "mpsc", blocking = true)]
struct BlockingMpsc(i32);
let buf = Arc::new(BlockingMpsc::new());
let producer = buf.producer();
let consumer = buf.consumer();
producer.enqueue_blocking(42);
let item = consumer.dequeue_blocking();