How To Make A Custom Type Iterable In Rust
These articles are AI-generated summaries. Please check the original sources for full details.
How To Make A Custom Type Iterable In Rust
This article explains how to implement IntoIterator, Iterator, and FromIterator traits for a heapless vector (ArrayVec) using MaybeUninit<T> to avoid heap allocation. The implementation enables use in for loops and with iterator combinators.
Why This Matters
Heapless data structures are critical in embedded systems where heap allocation is unsafe or unavailable. However, making such types iterable requires careful handling of memory safety, ownership, and drop semantics. Failing to implement Drop for custom iterators can lead to double-frees or memory leaks, with costs ranging from subtle bugs to system crashes.
Key Insights
- “8-hour App Engine outage, 2012” (not in context, omitted)
- “Sagas over ACID for e-commerce” (not in context, omitted)
- “Temporal used by Stripe, Coinbase” (not in context, omitted)
- “ArrayVec uses
MaybeUninit<T>for stack-based memory management” - “Implementing
DropforArrayVecIntoIterprevents double-free errors” - “Iterator traits enable
for item in &vecsyntax and compatibility withmap,filter”
Working Example
use core::mem::MaybeUninit;
#[derive(Debug)]
pub struct ArrayVec<T, const N: usize> {
values: [MaybeUninit<T>; N],
len: usize,
}
impl<T, const N: usize> ArrayVec<T, N> {
pub fn new() -> Self {
ArrayVec {
values: [const { MaybeUninit::uninit() }; N],
len: 0,
}
}
pub fn try_push(&mut self, value: T) -> Result<(), T> {
if self.len == N {
return Err(value);
}
self.values[self.len].write(value);
self.len += 1;
Ok(())
}
pub fn as_slice(&self) -> &[T] {
unsafe { core::slice::from_raw_parts(self.values.as_ptr() as *const T, self.len) }
}
}
pub struct ArrayVecIntoIter<T, const N: usize> {
values: [MaybeUninit<T>; N],
len: usize,
index: usize,
}
impl<T, const N: usize> Iterator for ArrayVecIntoIter<T, N> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
if self.index >= self.len {
return None;
}
let i = self.index;
self.index += 1;
unsafe { Some(self.values[i].assume_init_read()) }
}
}
impl<T, const N: usize> Drop for ArrayVecIntoIter<T, N> {
fn drop(&mut self) {
for i in self.index..self.len {
unsafe { self.values[i].assume_init_drop(); }
}
}
}
impl<T, const N: usize> IntoIterator for ArrayVec<T, N> {
type Item = T;
type IntoIter = ArrayVecIntoIter<T, N>;
fn into_iter(self) -> Self::IntoIter {
let this = core::mem::ManuallyDrop::new(self);
let values = unsafe { core::ptr::read(&this.values) };
let len = unsafe { core::ptr::read(&this.len) };
ArrayVecIntoIter { values, len, index: 0 }
}
}
Practical Applications
- Use Case:
ArrayVecin embedded systems for stack-based collections without heap allocation. - Pitfall: Forgetting to implement
Dropfor custom iterators can cause memory leaks or double-frees.
References:
Continue reading
Next article
Introducing Layrr - Framer for your Codebase!
Related Content
Amnosia: A Rust-Based CLI for Terminal-Integrated Task Management
Gennaro Biondi develops Amnosia, a Rust CLI tool that automates task visibility by integrating directly into the terminal shell startup.
Magika 1.0: AI-Powered File Type Detection in Rust
Google released Magika 1.0, a Rust-based file type detection system achieving 99% average precision and recall across over 200 file types.
Rust CI: Security, Dependency Policy, Coverage Gate, and Fast Builds
GitHub Actions workflow for Rust enforces 80% test coverage and security checks.