In this lesson, you will learn how to use const generics to create flexible, stack-allocated buffers that adapt their size at compile time without sacrificing type safety.
The Problem: Hardcoded Sizes
In Rust, defining a fixed-size buffer usually looks like this: [u8; 1024]. While simple, this is rigid. If you need a buffer of 512 bytes, you have to write a completely new struct or type. You might be tempted to use a Vec<u8>, but that moves data to the heap, which isn't ideal for embedded systems or high-performance, stack-only applications.
The Solution: Const Generics
Const generics allow us to use a constant value (like an integer) as a generic parameter. Instead of hardcoding 1024, we define our struct to accept a generic constant N representing the size of the array.
struct Buffer<const N: usize> {
data: [u8; N],
}
fn main() {
let small_buf = Buffer::<16> { data: [0; 16] };
let large_buf = Buffer::<1024> { data: [0; 1024] };
}
Why This Matters
By using const N: usize, you are telling the Rust compiler: "This struct is a blueprint, and the exact size of the array will be determined when the code is compiled."
This provides several massive benefits:
- Zero-Cost Abstractions: There is no runtime overhead. The compiler generates specialized machine code for every size you use.
- Type Safety:
Buffer<16>andBuffer<1024>are distinct types. You cannot accidentally pass a 16-byte buffer into a function expecting 1024 bytes; the compiler will catch it immediately. - Efficiency: Because the size is known at compile time, the compiler can place these buffers directly on the stack, avoiding heap allocation entirely.
You can now write reusable logic—like a hashing algorithm or a network packet handler—that works for any buffer size while keeping the exact memory layout locked down for the specific use case.
Key Takeaway: Const generics allow you to write generic, reusable code that maintains the performance and safety of fixed-size stack arrays by binding buffer lengths to the type itself at compile time.