27

I have a vec of some struct type and I want to change some field of the first element in the vector. How can I do this?

Example:

struct SomeType {
    some_value: i32,
}

fn main() {
    let mut vec = Vec::new();
    let mut t = SomeType { some_value: 45 };
    vec.push(t);

    println!("Old value: {}", vec.first().unwrap().some_value);
    vec.first().unwrap().some_value += 1;
    println!("New value: {}", vec.first().unwrap().some_value);
}

This fails to compile:

error: cannot assign to immutable field
  --> vec.rs:15:2
   |
15 |    vec.first().unwrap().some_value += 1;
   |    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot mutably borrow immutable field

I can't get my head around the mutability stuff in Rust yet; what would be the correct approach here?

1
  • Modify vector using a loop: for i in vec.iter_mut() { *i = * i + 2; }
    – manojadams
    Commented Mar 17, 2023 at 7:24

5 Answers 5

33

To mutate the first element of the vector you'd usually get a reference to that element. In other words, a reference into the vector. And being a normal structure the vector needs to have a method that would provide you with the reference.

Thing is, a reference into a vector means you can do something with the insides of the vector, read them or modify them in some way. Rust doesn't know the details, it just knows that while you're holding that reference, you can do stuff.

And with just that limited information the borrow checker of Rust tries to stop you from shooting yourself in the foot. It says: if you're going to read the vector, fine, you can read it any way you want, you can even make some other function read it, or two functions, or five. But you can't modify the vector while you're reading it, it isn't safe, it leads to bugs. So, you can have as many read-only references into the vector as you want, but only if and when you're not holding any writeable references into it. If you do hold a writeable reference, then there can be only one such reference at a time.

Thus the kind of reference matters. And that is why the vector has the two methods that give you the first element: first and first_mut.

So here

let mut vec = Vec::new();

your vector is already mutable. And coming from other languages you might work from intuition that if the vector is mutable once, it is mutable always. Kind of like const value in C++ or immutable in D. It's either mutable, or it's not.

But in Rust you might want immutable references into mutable structure. For instance, you might want one thread to work on one element of a vector and another thread on another element, followed by vector mutation after a join. And if you'd rather keep the borrow checker's safety belt on, then the simplest way to have multiple references is to keep them immutable.

let mut v = vec![1, 2];
let (v0, v1) = std::thread::scope(|s| {
    let v0 = s.spawn(|| v.first().unwrap() + 1); // Immutable reference
    let v1 = s.spawn(|| &v[1] + 1); // Immutable reference
    (v0.join().unwrap(), v1.join().unwrap())});
v[0] = v0; v[1] = v1; // Mutating the vector after the join

(crossbeam_utils, std)

That is why methods like first return immutable references and to get a mutable reference you need to explicitly opt in by using a different method.

P.S. So was that any help?

P.S. Using usize indexes instead of references is often helpful when the algorithm requires a complex combination of reads and writes.
IndexMap usize indexes can also be used like that.

1
  • Thanks for the detailed explanation! Indeed coming from C++ I tried handling things similar to const, now I understand it better. Commented Apr 22, 2017 at 16:15
18

Here's equally working code samples:

vec[0].some_value += 1;

vec.first_mut().unwrap().some_value += 1;

The issue with code that is shown in question is that first() returns an (immutable) reference to first element but mutable reference is required.

Indexing ([0]) works like this:

  1. vec derefs to slice
  2. indexing on slice calls index_mut method from IndexMut trait
  3. index_mut method returns mutable reference (&mut someType)
1
  • Thanks for the examples, that's easier than expected. I hope you don't mind that I accepted ArtemGr's answer, since he provided a very detailled explanation. Commented Apr 22, 2017 at 16:18
9

I think you're looking for &mut when taking reference.

#[derive(Debug)]
struct Character{
    name: String,
}

fn main() {
    let mut hobbits = vec![
        Character{name:String::from("Sam")},
        Character{name:String::from("Merry")},
        Character{name:String::from("Pepper")},
    ];

    {
        let third = &mut hobbits[2];
        third.name = String::from("Pippin");
    }

    println!("{:?}", hobbits);
}

Note {} around mutable element reference. It is required to limit mutable reference scope. Can't have mutable and immutable references at the same time: println! would fail while third is still in scope.

5

This is an old question. I recently started with Rust and I'd like to try to answer.

To update the first element, I'd do this:

let mut st = &mut vec[0];
st.some_value += 1;

or just:

vec[0].some_value += 1;

To entirely replace the first element, I can do this:

std::mem::replace(&mut vec[0], SomeType { some_value: 111 });
1

You can do this by this way as well:

fn changeValues(mut list: Vec<i32>){
        println!("Before value change: {:?}", list.get(0).unwrap());
        list[0] = 3;
        println!("After value change: {:?}", list.get(0).unwrap());
    }

Not the answer you're looking for? Browse other questions tagged or ask your own question.