An immutable collection preserves its existing value when an operation adds or changes an element: the operation produces another value instead of changing the original in place. This makes changes easier to reason about and can reduce unexpected side effects. It can also cost memory, CPU time, and design effort, so it is a choice rather than a rule. section 9.4 section 9.2
In Rust, an immutable binding is not automatically an immutable-collection API. Standard collections such as Vec and HashMap offer mutable operations; an immutable binding prevents their use on that value, but it does not make the collection persistent. Interior-mutability containers such as RefCell can also permit changes behind an immutable binding. section 9.4 section 9.5
Choose how strongly the collection should encourage immutable use:
- Borrow existing data when no change is needed. Clone or use ToOwned to obtain an owned value when a new version is needed; avoid copying merely to sidestep ownership decisions. section 9.6 section 10.5
- Wrap data in Cow when copying should wait until modification. A method can return a new wrapper while leaving the source unchanged. section 9.7
- Use im or rpds for collections optimized for immutable use. Their APIs still allow mutation, so the choice to preserve earlier values remains yours. section 9.8
Listing 9.8 Wrapping Cow in a struct
#[derive(Debug, Clone)] #1
struct CowList<'a> {
cows: Cow<'a, [String]>, #2
}
impl<'a> CowList<'a> {
fn add_cow(&self, cow: &str) -> Self { #3
let mut new_cows = self.clone(); #4
new_cows.cows.to_mut().push( #5
cow.to_string() #5
); #5
new_cows #6
}
}
impl Default for CowList<'_> {
fn default() -> Self {
CowList {
cows: Cow::from(vec![]),
}
}
}
CowList::add_cow clones the wrapper, calls to_mut() on the new wrapper, and returns it. The caller receives an extended list without altering the list it called the method on. section 9.7
Listing 9.9 Using im to create a Vector
use im::vector;
let shopping_list =
vector!["milk", "bread", "butter", "cheese", "eggs"];
let mut updated_shopping_list = shopping_list.clone(); #1
updated_shopping_list.push_back("grapes"); #2
dbg!(&shopping_list);
dbg!(&updated_shopping_list);
The im Vector example clones a shopping list before calling push_back on the clone. Both versions remain available. The explicit mutation of the clone also shows that im does not enforce immutable use. section 9.8.1
Prefer the simplest approach that fits the workload. A clone can be a deliberate way to preserve an earlier version, but repeated copies of ordinary collections may waste time and memory. Do not treat an immutable binding—or a crate optimized for immutability—as proof that no mutation is possible. section 10.5 section 9.8.1























