Elements can be assigned
You change an element by assigning to its index. The target of the assignment is the same bracket expression you use for reading:
let mut our_array = [50, 40, 30];
our_array[0] = 15;
our_array[1] = -1;
// our_array is now [15, -1, 30]
The array stays the same length and keeps its element type. Only the values at those positions are replaced. The same code works on a Vec.
The key detail is let mut. In Rust a variable is immutable unless declared with mut, and that covers every element inside it. Without mut, a[0] = 5; doesn't compile: the compiler says it cannot assign to a[_] because a is not declared as mutable, and suggests adding mut.
Compound assignment
Operators such as +=, -= and *= work on elements. ages[1] += 5 reads the element, adds 5 and stores the result back in the same position. It is shorthand for ages[1] = ages[1] + 5.
The type must match
An element can only be replaced by a value of the element type. This matters for text. A vector created with "mango".to_string() holds String values, owned and growable text, not &str slices. Assigning a bare literal to one of its slots is an error:
expected `String`, found `&str`
Convert the literal first with .to_string(), as in fruits[0] = "apple".to_string();. A literal like "apple" has type &str, a borrowed view of text stored in the program, so it has to become an owned String before it can live in a Vec<String>.
The lesson's exercise
The starting code is:
let mut fruits = vec!["mango".to_string(), "kiwi".to_string(), "grapefruit".to_string()];
let mut ages = vec![14, 32, 50, 21];
Both are declared with mut, so their elements can change.
- Replace the first fruit. Assign to
fruits[0]with"apple".to_string(). The vector becomesapple,kiwi,grapefruit. - Add 5 to the second age. Index 1 holds 32, and
ages[1] += 5makes it 37. - Verify both changes. Print
1if the check passes and0otherwise. In Rust,if condition { 1 } else { 0 }is an expression, so it can be passed straight toprintln!. The first check isages[1] == 37. The second joins three comparisons with&&, one per fruit. AStringcan be compared with a string literal using==.
let mut fruits = vec!["mango".to_string(), "kiwi".to_string(), "grapefruit".to_string()];
let mut ages = vec![14, 32, 50, 21];
fruits[0] = "apple".to_string();
ages[1] += 5;
println!("{}", if ages[1] == 37 { 1 } else { 0 });
println!("{}", if fruits[0] == "apple" && fruits[1] == "kiwi" && fruits[2] == "grapefruit" { 1 } else { 0 });
Both lines print 1. The two branches of each if have the same type, which is required for it to be used as a value.
Common mistakes
- Forgetting
mut. Assignment to an element of an immutable binding doesn't compile. - Assigning a
&strto aStringslot. Add.to_string()(or useString::from). - Using
=where you mean==.=assigns, and==compares. The checks must use==. - Wrong index. The first item is at index 0 and the second at index 1. An index past the end panics at run time.
- Expecting a change in a copy. Arrays of integers are copied when assigned to a new variable. After
let mut b = a;, changingb[0]doesn't changea. A vector is moved, not copied, on assignment, so the old name can't be used afterwards.
Key takeaways
- Assign to an element with
v[i] = value, and update it in place withv[i] += n. - The variable must be declared with
let mut. - The new value must have the element type, so a
Vec<String>needs.to_string()on literals. if ... { 1 } else { 0 }is a value, which makes small checks easy to print.
