What's the best way to make this simple vector modification compile?

#1

Hello

I’m new to Rust.

fn main() {
    let mut numbers = vec![1,2,3];
    let first : & mut i32 = & mut numbers[0];
    *first = 7;
    println!("{:?}", numbers);
    *first = 8;
    println!("{:?}", numbers);
}

cannot borrow numbers as immutable because it is also borrowed as mutable
–> src/main.rs:5:22

What’s the best way to make this example compile?

Or should I simply avoid modifying vector elements via pointers entirely, and always modify them via indexing?

In other languages I often have something like

 cur_user = &users[id];
 ...
 cur_user.processed = true;
#2

You could use brackets/scopes like this:

fn main() {                                                                                             
    let mut numbers = vec![1, 2, 3];                                                                    
    {                                                                                                   
        let first: &mut i32 = &mut numbers[0];                                                          
        *first = 7;                                                                                     
    }                                                                                                   
    println!("{:?}", numbers);                                                                          
    {                                                                                                   
        let first: &mut i32 = &mut numbers[0];                                                          
        *first = 8;                                                                                     
    }                                                                                                   
    println!("{:?}", numbers);                                                                          
}                                                                                                       
#3

Think of &mut as a lock for exclusive access. For as long as the exclusive reference exists, nothing else at all can happen to the object.

2 Likes
#4

Thanks. So should I simply avoid modifying vector elements via pointers entirely, and always modify them via indexing?

#5

Rust doesn’t have syntax for passing by reference vs passing by value. In Rust & signifies borrowing, and is tied to the concept of ownership.

In some ways borrows are similar to pointers, but that’s not the point of them. For example, Box<u8> is also a pointer-like construct (but owned, not borrowed), and OTOH &str is implemented as a two-element struct passed by value.

If you’re coming from C or C++, you’re likely to overuse references until you “get” ownership. Keep in mind that in Rust you don’t have to worry about copying anything by accident (anything that is expensive will force you to call .clone()), and things that don’t look like pointers may be pointers or thin wrappers around pointers.

It’s hard to answer your question directly, because the answer is probably neither. If the 0-th element is special, or you just want to pass that one element to another function, then it’d make sense to borrow it. But more generally, indexing on vectors is avoided. Vectors tend to be modified via iterators:

for item in &mut vec {*item = 1}

or not even modified in-place, but created from iterators:

(0..1000).map(|x| x*2).collect::<Vec<_>>();
2 Likes
#6

This lines reads as: “I want a unique reference to an integer, thus obtained from a unique reference into the vector” (that is what &mut really means).

Rust takes very seriously this kind of uniqueness guarantees, meaning that while the variable / binding first exists, it will ensure there is no other way to access numbers, thus forbidding your first read attempt of numbers (since first is used afterwards, the exclusive borrow cannot have ended up until that point).


If you really want this kind of code (but as @kornel said, you will end up changing your coding patterns and habits, once you grasp ownership nd borrowing), you can get it with Interior Mutability, which enables mutating the vector contents through non-exclusive references (& _):

use ::std::{
    cell::Cell,
    iter::FromIterator,
};

fn main ()
{
    let numbers = Vec::from_iter(
        [1, 2, 3]
            .iter().cloned()
            .map(Cell::new)
    );
    let first: &Cell<i32> = &numbers[0];
    first.set(7);
    println!("{:?}", numbers);
    first.set(8);
    println!("{:?}", numbers);
}
1 Like
#7

You don’t need braces now as our mighty NLL automatically do it for us! All we need is just duplicate the first decls so NLL can detect that the previous one is not in use.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=93db8b2457f0a4b4866f8ab0947c916b

1 Like