Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use multiple items in a Vec at a time in Rust?

Tags:

rust

I've been fighting with the borrow checker for a little bit... the gist of what I want to do is this:

#[derive(Debug)]
struct SomeStruct {
    value: String,
}

impl SomeStruct {
    fn new(value: &str) -> SomeStruct {
        SomeStruct { value: value.to_string() }
    }

    fn change_value(&mut self, new_value: &str) {
        self.value = new_value.to_string();
    }
}

fn main() {
    let mut my_vec = vec![
        SomeStruct::new("foo"),
        SomeStruct::new("bar"),
    ];

    my_vec[0].change_value(my_vec[1].value.as_str());
}

This is a very generic version of a problem I'm having. Here's the stderr:

error[E0502]: cannot borrow `my_vec` as immutable because it is also borrowed as mutable
  --> src/main.rs:22:30
   |
22 |     my_vec[0].change_value(my_vec[1].value.as_str());
   |     ------                 ^^^^^^                  - mutable borrow ends here
   |     |                      |
   |     |                      immutable borrow occurs here
   |     mutable borrow occurs here

So the borrow checker doesn't allow me to borrow the vector twice (once as mutable, and then again as immutable), which I understand. But the frustrating thing is that I want to modify one element in the vector, and only read another. I'm new to Rust (surprise!), and I'm not sure I've wrapped my mind around all its details and design choices. But this is something that feels like it should work, yet doesn't. What am I missing, and what can I do to get this (or comparable behavior) to work? Any help is greatly appreciated!

like image 351
tronje Avatar asked Dec 22 '16 14:12

tronje


2 Answers

I agree, this is slightly confusing. So let's first see, why this is not allowed by the Rust compiler.

Why it's not allowed

The index operator [] is something that can be overloaded, which means that users of the language can specify how it works. Rust tries to minimize the number of types of which the compiler has some special knowledge. As a consequence and despite its popularity, Vec<T> is just a normal type defined by a library. You could write your own Vec<T> without telling the compiler about it!

Vec<T> also overloads the index operator, to allow indexing a vector. But since the overload could do anything, it could always return the first element of the vector! And if you assume the index operator would do such a strange thing, this code shouldn't be allowed:

my_vec[0].change_value(my_vec[1].value.as_str());

Because my_vec[0] and my_vec[1] reference the same value.

How to make it work

Of course, the index operator is not implemented in such a stupid fashion and we know that. In order to get two references to different elements of the vector (where at least one is mutable), we have to use some special functions instead of the index operator. And there are quite some ways to do it:

  • split_first_mut()
  • split_at_mut()
  • iter_mut() which returns an iterator over mutable references
  • ...

I can't really tell you what method to use, because I don't know your exact use case. But just to fix your example, you can write:

let (head, tail) = my_vec.split_first_mut();
head.change_value(tail[0].value.as_str());
like image 189
Lukas Kalbertodt Avatar answered Sep 28 '22 17:09

Lukas Kalbertodt


You're right, you can't borrow an object both immutably and mutably at the same time; in order to get this working you can do the following:

let new_value = my_vec[1].value.clone();
my_vec[0].change_value(&new_value);

When you clone() the value from myvec[1], you are no longer borrowing myvec and are free to use the new_value in the next line.

like image 29
ljedrz Avatar answered Sep 28 '22 17:09

ljedrz