a[a[0]] = 9 doesn’t work in Rust, why?

Photo by Kieran Wood on Unsplash

I recently start to learn Rust, and try to apply it in some problem that I have worked before. But I noticed certain things that can be trivial in other languages stops to be a trivial topic. On the contrary, it can be quite difficult.

fn main() {
let mut a = vec![1,2,3,4,5];
a[a[0]] = 9;
println!("{:?", a);
}

Although the above code seems pretty naive, it reads the first element a[0] and use it to write into another element a[1] . If you try to compile it, we got the following error:

error[E0502]: cannot borrow `a` as immutable 
because it is also borrowed as mutable

a[a[0]] = 9;
--^----
| |
| immutable borrow occurs here
mutable borrow occurs here
mutable borrow later used here

I don’t blame you if you have no idea what these borrow means in the error message. Say we just ignore it and try something different.

Fooling around to make it work

Ok, let’s try to print it first and see if we made any syntax mistake:

  println!("{}", a[a[0]]);  // 2

Yes, it’s working. Getting the value from a[a[0]] has no problem.

Ok, let’s try to write to it:

a[1] = 9;  
println!("{}", a[a[0]]); // 9

The code works as well; we first write to one of the element and then read it out.

Here comes the fix by accidentally introducing a new variable i :

  let i = a[0];
a[i] = 9;
println!("{:?}", a[a[0]]); // 9

The above works!! Wow, what a progress! So what have we done? We put a[0] to a local variable, and then use it as an index to write into the vector.

What have we done? Not sure!

Well, I wouldn’t want to sugar code it. If you pick any Rust book, it would tell you the story much better than me. So I wouldn’t go into details to pretend I know what’s going on. I’ll only show you what’s on the surface.

Rust doesn’t allow an immutable borrow and mutable borrow applied to the same value in one scope. a[0] is immutable borrow, since it reads out something; whereas a[0] = 9 is a mutable borrow, since it writes in something. So a[a[0]] = 9 wouldn’t compile at all!

But to a newbie, what is the difference between a[a[0]] and a[i] where i = a[0] . Man, this is hard. To be honest, if you are not prepared for this type of issue, you’ll be stuck in using Rust quite soon, especially if you think you are good at programming using other languages.

Long story short. i holds a new value, because a is a vector of usize (unsigned integer) and taking a value out of the vector using a[0] producing a copy of the value. You could think of this usize as a primitive value in other languages, although Rust does not have many primitive types. Also Rust does not use this word primitive, it only checks if the type implements a Copy trait (and Clone trait) or not. Anyway, for now, just need to know the a[0] is cloned and copied into the local variable i .

Now the problem changes: a[i] = is a mutable borrow but there’s no immutable borrow of a any more. And we see the magic actually happens in let i = a[0]. Is that simple? What happens if we are not dealing with usize type, what if we store an object in the vector. Let’s try it:

fn main() {
let mut a = vec!["abc".to_string()];
let i = a.last();
a.push("def".to_string());
}

Instead of a “primitive” copiable value, we use String type instead. This is exactly what one of the stack overflow ticket mentioned. The compiler shows the same error once again, even after we write the magic let i statement.

The answer is that, because this time i does not get a copied value from a String , it still holds an immutable borrow of a. Then when we go to a.push which is a mutable borrow, we run into the problem again. The ticket also mentions a fix:

  let i = a.last().cloned();

The fix seems giving us the same approach of making i a copy. But this is not same as copying an element content this time. According to the documentation, cloned is to clone an entire iterator of a. That is how i gets to own a different String letting a off owning the vector.

Conclusion

Notice a[a[0]] = 9 statement won’t work in Rust. This is something we really have to understand before using Rust, because we have been writing this type of statement for decades, thus some systematic brain-wash is definitely needed to re-educate us the “correct” way of doing things. Hopefully this is the correct way!

--

--

--

Front-end Engineer, book author of “Designing React Hooks the Right Way” sold at Amazon.

Love podcasts or audiobooks? Learn on the go with our new app.

Recommended from Medium

JavaScript: Overview and Case-study

Vue.js — Pros, cons and things I learnt along the way…

🍅Pomodoro Clock ⏱with React

LEARNING HOW TO CODE

How To Make Siri your Perfect Home Companion With Devices not Supported by Apple Homekit

Why I don’t use For Loops anymore

Lazy loading images with Javascript

Chapter 4 Interacting with Web Pages

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
Fang Jin

Fang Jin

Front-end Engineer, book author of “Designing React Hooks the Right Way” sold at Amazon.

More from Medium

Exploring rustc : how does Rust implement type inference

Rust WebAssembly — Sharing data between WebWorkers

Right Sibling Tree | Rust

A Tree Structure implemented in Rust.