| name | rust-memory |
| description | Minimize allocations and cloning using references, Cow, and smart pointers. Use when optimizing hot paths or memory-constrained code. |
Memory Efficiency
Patterns to minimize allocations and copies in Rust.
Avoid Unnecessary Cloning
fn process(data: Vec<String>) -> Vec<String> {
data.clone().iter().map(|s| s.to_uppercase()).collect()
}
fn process(data: Vec<String>) -> Vec<String> {
data.into_iter().map(|s| s.to_uppercase()).collect()
}
fn process(data: &[String]) -> impl Iterator<Item = String> + '_ {
data.iter().map(|s| s.to_uppercase())
}
Prefer &str over String
fn greet(name: String) {
println!("Hello, {}!", name);
}
fn greet(name: &str) {
println!("Hello, {}!", name);
}
fn greet(name: impl AsRef<str>) {
println!("Hello, {}!", name.as_ref());
}
Pre-allocate Collections
let mut v = Vec::new();
for i in 0..1000 {
v.push(i);
}
let mut v = Vec::with_capacity(1000);
for i in 0..1000 {
v.push(i);
}
let mut s = String::with_capacity(4000);
let mut map = HashMap::with_capacity(100);
Copy-on-Write (Cow)
use std::borrow::Cow;
fn normalize_path(path: &str) -> Cow<'_, str> {
if path.contains("//") {
Cow::Owned(path.replace("//", "/"))
} else {
Cow::Borrowed(path)
}
}
let normalized = normalize_path("/home/user");
let fixed = normalize_path("/home//user");
struct Config<'a> {
name: Cow<'a, str>,
}
impl<'a> Config<'a> {
fn new(name: &'a str) -> Self {
Self { name: Cow::Borrowed(name) }
}
fn with_prefix(mut self, prefix: &str) {
.name = Cow::((, prefix, .name));
}
}
Smart Pointers
use std::sync::Arc;
use std::rc::Rc;
let data = Arc::new(expensive_data);
let data_clone = Arc::clone(&data);
let data = Rc::new(expensive_data);
let data_clone = Rc::clone(&data);
let large_data = Box::new([0u8; 1_000_000]);
Zero-Copy with Bytes
use bytes::Bytes;
let data = Bytes::from(vec![0u8; 1024]);
let slice = data.slice(0..512);
use bytes::BytesMut;
let mut buf = BytesMut::with_capacity(1024);
buf.extend_from_slice(b"hello");
buf.extend_from_slice(b" world");
let frozen = buf.freeze();
SmallVec for Small Collections
use smallvec::SmallVec;
let mut results: SmallVec<[Result; 8]> = SmallVec::new();
results.push(Ok(1));
results.push(Ok(2));
Avoid Intermediate Collections
let sum: i32 = data
.iter()
.filter(|&&x| x > 2)
.collect::<Vec<_>>()
.iter()
.map(|&&x| x * 2)
.sum();
let sum: i32 = data
.iter()
.filter(|&&x| x > 2)
.map(|&x| x * 2)
.sum();
String Building
let s = String::new() + "hello" + " " + "world";
let mut s = String::with_capacity(11);
s.push_str("hello");
s.push(' ');
s.push_str("world");
let s = format!("hello {} world", name);
let parts = vec!["hello", "world"];
let s = parts.join(" ");
Take and Replace
struct Container {
data: Option<Vec<u8>>,
}
impl Container {
fn take_data(&mut self) -> Option<Vec<u8>> {
self.data.take()
}
}
let mut value = 5;
let old = std::mem::replace(&mut value, 10);
assert_eq!(old, 5);
assert_eq!(value, 10);
Guidelines
- Use
&str instead of String for parameters
- Pre-allocate with
with_capacity() when size is known
- Use
Cow for "maybe modified" returns
- Prefer
Arc::clone(&x) over x.clone() for clarity
- Use
Bytes for zero-copy buffer sharing
- Chain iterators instead of collecting intermediates
- Use
take() and replace() to avoid cloning
Examples
See hercules-local-algo/src/pipeline/ for memory-efficient patterns.