How does smallvec::SmallVec::from_buf differ from from_slice for initializing from existing data?
from_buf takes ownership of an existing array to initialize a SmallVec without copying, while from_slice copies data from a slice into a new allocation. from_buf is the zero-copy initialization path—it consumes an array you already have and uses it as the initial storage. from_slice always copies: it iterates over the slice elements and clones or copies them into the SmallVec's internal storage. Use from_buf when you have an owned array and want to avoid allocation; use from_slice when you have borrowed data and need to create an owned SmallVec.
Creating SmallVec from Owned Array with from_buf
use smallvec::SmallVec;
fn main() {
// from_buf takes ownership of an array
// No copying occurs - the array becomes the SmallVec's initial storage
let array: [i32; 4] = [1, 2, 3, 4];
let vec: SmallVec<[i32; 4]> = SmallVec::from_buf(array);
println!("SmallVec: {:?}", vec.as_slice());
// The array is consumed - it now belongs to the SmallVec
// This is zero-copy for small arrays that fit inline
// The SmallVec uses the array's memory directly
// For arrays larger than inline capacity, from_buf still works
// but the semantics depend on the SmallVec's capacity
}from_buf consumes the array—no allocation or copying happens for the initial content.
Creating SmallVec from Slice with from_slice
use smallvec::SmallVec;
fn main() {
// from_slice copies elements from a borrowed slice
// Each element is cloned/copied into the SmallVec
let slice: &[i32] = &[1, 2, 3, 4, 5];
let vec: SmallVec<[i32; 4]> = SmallVec::from_slice(slice);
println!("SmallVec: {:?}", vec.as_slice());
// Elements are copied into the SmallVec
// The slice can be larger than inline capacity
// SmallVec will heap-allocate if needed
let large_slice: &[i32] = &[1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
let vec: SmallVec<[i32; 4]> = SmallVec::from_slice(large_slice);
println!("Length: {}", vec.len()); // 10
// Inline capacity is 4, so this spills to heap
}from_slice always copies elements—it works with borrowed data and produces an owned SmallVec.
Ownership and Copying Semantics
use smallvec::SmallVec;
fn main() {
// from_buf: takes ownership, zero-copy
let array = [1, 2, 3, 4];
let vec: SmallVec<[i32; 4]> = SmallVec::from_buf(array);
// array is moved into vec
// No copying of elements
// from_slice: borrows, copies elements
let slice: &[i32] = &[1, 2, 3, 4];
let vec: SmallVec<[i32; 4]> = SmallVec::from_slice(slice);
// slice is borrowed
// Each i32 is copied into vec
// For Copy types, from_slice copies each element
// For Clone types, from_slice clones each element
let strings: &[String] = &["hello".to_string(), "world".to_string()];
let vec: SmallVec<[String; 2]> = SmallVec::from_slice(strings);
// Each String is cloned into the SmallVec
// Compare to from_buf which would take ownership:
// let array = ["hello".to_string(), "world".to_string()];
// let vec: SmallVec<[String; 2]> = SmallVec::from_buf(array);
// No cloning - strings are moved
}from_buf is move-based; from_slice is copy/clone-based.
Performance Implications
use smallvec::SmallVec;
fn main() {
// from_buf: O(1) - just wraps the array
// from_slice: O(n) - copies n elements
// Benchmark example (conceptual):
// from_buf - no per-element work
let array: [i32; 100] = [0; 100];
let start = std::time::Instant::now();
let vec: SmallVec<[i32; 100]> = SmallVec::from_buf(array);
let from_buf_time = start.elapsed();
println!("from_buf: {:?}", from_buf_time);
// from_slice - copies each element
let slice: &[i32] = &[0; 100];
let start = std::time::Instant::now();
let vec: SmallVec<[i32; 100]> = SmallVec::from_slice(slice);
let from_slice_time = start.elapsed();
println!("from_slice: {:?}", from_slice_time);
// For Copy types, from_slice is still fast (memcpy-like)
// For Clone types with expensive clones, from_buf is much faster
// Example with expensive clones:
// struct BigData([u8; 1024]);
// impl Clone for BigData {
// fn clone(&self) -> Self { /* expensive copy */ }
// }
//
// from_buf with [BigData; N] - O(1)
// from_slice with &[BigData] - O(N) with expensive clones
}from_buf is O(1) for initialization; from_slice is O(n) where n is slice length.
Working with Heap Spillover
use smallvec::SmallVec;
fn main() {
// from_buf respects inline capacity
// Array fits inline - uses array directly
let array = [1, 2, 3];
let vec: SmallVec<[i32; 4]> = SmallVec::from_buf(array);
println!("Inline: len={}, capacity={}", vec.len(), vec.capacity());
// Capacity is at least 3, likely inline
// from_slice handles overflow to heap
let slice: &[i32] = &[1, 2, 3, 4, 5];
let vec: SmallVec<[i32; 4]> = SmallVec::from_slice(slice);
println!("Heap spillover: len={}, capacity={}", vec.len(), vec.capacity());
// len=5, capacity >= 5 (heap allocated)
// from_buf with array larger than inline capacity:
// If array size > inline capacity, behavior depends on implementation
// Generally: from_buf(array) uses the array as-is if it fits
// For arrays larger than capacity, it may still use the array inline
// Key insight: from_buf's array size and SmallVec's capacity
// are separate concerns
}Both methods handle heap spillover, but from_buf takes what you give it.
Related Methods: from_buf_and_len
use smallvec::SmallVec;
fn main() {
// from_buf_and_len initializes from array with explicit length
// Useful when array is larger than initialized elements
let array: [i32; 8] = [1, 2, 3, 4, 5, 6, 7, 8];
// Create SmallVec with only first 3 elements "active"
let vec: SmallVec<[i32; 8]> = SmallVec::from_buf_and_len(array, 3);
println!("Length: {}", vec.len()); // 3
println!("Elements: {:?}", vec.as_slice()); // [1, 2, 3]
// The remaining elements (4, 5, 6, 7, 8) are in the array
// but not part of the SmallVec's logical content
// This is useful when you have an initialized array
// but only want to use part of it
// Compare:
let vec_full: SmallVec<[i32; 8]> = SmallVec::from_buf(array);
// Now vec_full has all 8 elements
// Use from_buf_and_len when:
// - Array is initialized but only prefix is valid
// - Working with buffers that have a "valid length"
}from_buf_and_len gives control over which array elements are part of the SmallVec.
Memory Layout
use smallvec::SmallVec;
fn main() {
// SmallVec<[T; N]> stores up to N elements inline
// When using from_buf:
// - If array size <= inline capacity (N): elements stored inline
// - The array becomes the inline storage
// When using from_slice:
// - If slice.len() <= N: elements copied to inline storage
// - If slice.len() > N: elements copied to heap
// Example: SmallVec<[i32; 4]>
// - Inline capacity: 4 elements (16 bytes for i32)
// - from_buf([1,2,3,4]): uses inline array
// - from_slice(&[1,2,3,4,5]): heap allocation needed
let inline: SmallVec<[i32; 4]> = SmallVec::from_buf([1, 2, 3, 4]);
println!("Inline capacity: {}", inline.capacity());
let heap: SmallVec<[i32; 4]> = SmallVec::from_slice(&[1, 2, 3, 4, 5]);
println!("Heap capacity: {}", heap.capacity());
// Both are valid SmallVecs, but memory location differs
}from_buf can guarantee inline storage if the array fits the capacity.
Use Cases
use smallvec::SmallVec;
fn main() {
// from_buf use cases:
// 1. Already have an owned array
let array = [1, 2, 3, 4];
let vec: SmallVec<[i32; 4]> = SmallVec::from_buf(array);
// 2. Zero-copy initialization from fixed-size data
fn process_fixed(data: [u8; 64]) -> SmallVec<[u8; 64]> {
SmallVec::from_buf(data)
}
// 3. Converting array-returning functions
fn get_array() -> [i32; 4] { [1, 2, 3, 4] }
let vec: SmallVec<[i32; 4]> = SmallVec::from_buf(get_array());
// from_slice use cases:
// 1. Converting borrowed data
fn from_ref(data: &[i32]) -> SmallVec<[i32; 4]> {
SmallVec::from_slice(data)
}
// 2. Copying from vectors or other collections
let vector = vec![1, 2, 3, 4];
let small: SmallVec<[i32; 4]> = SmallVec::from_slice(&vector);
// 3. Working with function returns that borrow
fn get_slice() -> &'static [i32] { &[1, 2, 3] }
let vec: SmallVec<[i32; 4]> = SmallVec::from_slice(get_slice());
}Choose based on ownership: from_buf when you own, from_slice when you borrow.
Type Constraints
use smallvec::SmallVec;
fn main() {
// from_buf requires the array type match the SmallVec's array type
// Correct: array type matches SmallVec parameter
let vec: SmallVec<[i32; 4]> = SmallVec::from_buf([1, 2, 3, 4]);
// This won't compile - wrong array size
// let vec: SmallVec<[i32; 4]> = SmallVec::from_buf([1, 2, 3]); // wrong size
// from_slice is more flexible - works with any length
let vec: SmallVec<[i32; 4]> = SmallVec::from_slice(&[1, 2, 3]);
let vec: SmallVec<[i32; 4]> = SmallVec::from_slice(&[1, 2, 3, 4]);
let vec: SmallVec<[i32; 4]> = SmallVec::from_slice(&[1, 2, 3, 4, 5]);
// from_slice accepts any slice length
// from_buf requires exact array type
// For dynamic initialization, from_slice is more convenient
fn dynamic_init(values: &[i32]) -> SmallVec<[i32; 16]> {
SmallVec::from_slice(values)
}
// For fixed-size buffers, from_buf is more efficient
fn fixed_init(values: [i32; 16]) -> SmallVec<[i32; 16]> {
SmallVec::from_buf(values)
}
}from_buf requires exact type matching; from_slice adapts to any slice length.
Converting Between SmallVec and Array
use smallvec::SmallVec;
fn main() {
// into_buf() is the reverse of from_buf()
// It extracts the array back if it fits inline
let vec: SmallVec<[i32; 4]> = SmallVec::from_buf([1, 2, 3, 4]);
// If the SmallVec hasn't spilled to heap:
if vec.spilled() {
println!("On heap, can't use into_buf directly");
} else {
// Actually into_buf works differently - let's show correct usage
let vec: SmallVec<[i32; 4]> = SmallVec::from_buf([1, 2, 3, 4]);
// into_buf() on SmallVec returns Box<[T]> for spilled, or array for inline
// Check the API for exact semantics
}
// into_vec() converts to a regular Vec
let vec: SmallVec<[i32; 4]> = smallvec![1, 2, 3, 4];
let regular_vec = vec.into_vec();
// from_vec() creates SmallVec from Vec
let vec = vec![1, 2, 3, 4];
let small: SmallVec<[i32; 4]> = SmallVec::from_vec(vec);
// from_slice vs from_vec:
// from_slice(&[T]) - copies elements
// from_vec(Vec<T>) - takes ownership of Vec's allocation
}SmallVec provides multiple conversion paths depending on your source data.
Practical Example: Buffer Handling
use smallvec::SmallVec;
// Simulating a buffer pool
struct BufferPool {
pool: Vec<[u8; 1024]>,
}
impl BufferPool {
fn new(count: usize) -> Self {
BufferPool {
pool: vec![[0u8; 1024]; count],
}
}
fn acquire(&mut self) -> SmallVec<[u8; 1024]> {
// from_buf takes ownership of array from pool
// Zero-copy - the array becomes the SmallVec's storage
self.pool.pop()
.map(|buf| SmallVec::from_buf(buf))
.unwrap_or_else(|| SmallVec::new())
}
fn release(&mut self, buf: SmallVec<[u8; 1024]>) {
// Note: exact semantics depend on SmallVec's implementation
// This is conceptual
if !buf.spilled() && buf.len() == 1024 {
// Could potentially return to pool
// (actual implementation would need into_inner or similar)
}
}
}
fn main() {
let mut pool = BufferPool::new(4);
// Acquiring a buffer - uses from_buf internally
let buffer = pool.acquire();
println!("Acquired buffer with capacity: {}", buffer.capacity());
// Alternative: using from_slice (copies data)
let data: &[u8] = &[1, 2, 3, 4];
let buffer: SmallVec<[u8; 1024]> = SmallVec::from_slice(data);
// Data is copied into buffer
// from_buf for owned buffers (zero-copy)
// from_slice for borrowed data (copies)
}Use from_buf for owned buffers; from_slice when copying from references.
Synthesis
Quick reference:
use smallvec::SmallVec;
fn main() {
// from_buf: Takes ownership of array, zero-copy
// - Signature: fn from_buf(buf: [T; N]) -> SmallVec<[T; N]>
// - O(1) - just wraps the array
// - Array is consumed (moved)
// - No copying of elements
// - Use when: you have an owned array
let array = [1, 2, 3, 4];
let vec: SmallVec<[i32; 4]> = SmallVec::from_buf(array);
// from_slice: Copies from slice
// - Signature: fn from_slice(slice: &[T]) -> SmallVec<[A]>
// - O(n) - copies n elements
// - Slice is borrowed
// - Elements are copied/cloned
// - Use when: you have borrowed data
let slice: &[i32] = &[1, 2, 3, 4];
let vec: SmallVec<[i32; 4]> = SmallVec::from_slice(slice);
// Key differences:
// 1. Ownership: from_buf takes owned, from_slice borrows
// 2. Copying: from_buf is zero-copy, from_slice copies
// 3. Flexibility: from_slice accepts any length, from_buf needs exact type
// 4. Performance: from_buf is O(1), from_slice is O(n)
// Related methods:
// from_buf_and_len(array, len) - use subset of array
// from_vec(vec) - take ownership of Vec
// from_slice(slice) - copy from slice
}Key insight: from_buf and from_slice represent two different ownership models. from_buf is for when you have an owned array and want to "promote" it to a SmallVec without copying—think buffer pools, stack-allocated data, or converting fixed-size returns. from_slice is for when you have borrowed data and need an owned SmallVec—think deserializing, copying from shared references, or working with data that lives elsewhere. The performance difference can be significant for large arrays or expensive-to-clone types, but the primary decision factor is ownership: if you own the array, use from_buf; if you're borrowing, use from_slice.
