Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I change fields of elements in vectors in Rust?

Tags:

rust

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?

like image 417
Philipp Ludwig Avatar asked Apr 21 '17 19:04

Philipp Ludwig


People also ask

How do you remove an element from a vector in Rust?

To remove all elements from a vector in Rust, use . retain() method to keep all elements the do not match. let mut v = vec![ "A", "warm", "fall", "warm", "day"]; let elem = "warm"; // element to remove v.

How do vectors work in Rust?

Vector is a module in Rust that provides the container space to store values. It is a contiguous resizable array type, with heap-allocated contents. It is denoted by Vec<T>. Vectors in Rust have O(1) indexing and push and pop operations in vector also take O(1) complexity.

How do I make a new vector in Rust?

In Rust, there are several ways to initialize a vector. In order to initialize a vector via the new() method call, we use the double colon operator: let mut vec = Vec::new();


Video Answer


3 Answers

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, 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. 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?

like image 79
ArtemGr Avatar answered Oct 19 '22 21:10

ArtemGr


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)
like image 12
dmitry_vk Avatar answered Oct 19 '22 21:10

dmitry_vk


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.

like image 7
Shchvova Avatar answered Oct 19 '22 21:10

Shchvova