Reference vector element

Hi.

fn main() {

    let v = vec![1, 2, 3, 4, 5];

    let a: &i32 = &v[1];

    let b: i32 = v[1];

    println!("The second element is {}", a);

    println!("The second element is {}", b);

}

Why both prints are equals, but variable 'a' has symbol '&' near i32 and near v[1] ? And why are these symbols (&) needed in this case?

Hi, you still seem to be fairly new around here, feel free to read the pinned post about code formatting.


The variable a has type &i32 which is a “reference to a 32-bit signed integer”
while variable b has type i32 which is a “32-bit signed integer”.

The & sign here—in the type—means “reference to”. The & sign in the expression &v[1] means “create a reference pointing to”. The expression v[1] denotes the second element od the vector v. Assigning it to b just copies the integer value out of the array, whereas creating a reference pointing to v[1] and assigning that reference to a internally just calculates the memory address of the integer inside of the vector.

let v = vec![1, 2, 3, 4, 5];


// create a reference pointing/referring to the second element of the vector `v`
let a: &i32 = &v[1]; 

// `a: &i32` means: 
// “a is a reference to a 32-bit signed integer”


// create a copy of the second element of the vector `v`
let b: i32 = v[1];

// `b: i32` means: 
// “b is a 32-bit signed integer”

Regarding printing, the println macro prints a reference to some type the same way it prints the type itself. The printing logic (for {} formatting) operates using the Display trait, which is implemented for reference types (here) by just ignoring the extra indirection, so you will not get any visible difference in the print-out.

The programming language Rust in general tries to make the difference between a type, say Foo, and the “type of references to Foo”, that is: &Foo, behave as similar as possible in situations where the difference doesn’t matter too much. For example you can call methods on an x: Foo by writing x.some_method() as well as you can call the same method on a y: &Foo as x.some_method(). [Of course there is a difference between i32 and &i32 and there are also (lots of) situations where that difference matters.]

If you read the book The Rust Programming Language, chapter 4 talks about references

2 Likes

I almost didn't understand anything, but thank you so much for this huge answer. I read chapter 4, but i don't understand why type annotation has symbol '&'. Thank u.

Yeah, I guess chapter 4 of the book doesn’t necessarily do the best job of introducing the concept of references without prior knowledge on e.g. pointers in C.

I’m looking if I find better introductory material somewhere.

Edit: Googling a bit, this article seems decent.

2 Likes

I mean, it has to do with whether you make a copy of the data in the vector, or if you just remember where in the vector the element is. There's not much point in using a reference in this case, because copying an integer is incredibly cheap, but if the vector contained elements that are expensive clone, it could be a lot cheaper to just keep a reference to the element than to make a clone of it.

Of course, making a reference requires that the vector stays valid, since the data is still stored in the vector. E.g. this doesn't compile:

// does not compile
fn main() {
    let mut v = vec![1, 2, 3, 4, 5];

    let a: &i32 = &v[1];
    v.clear();
    println!("The second element is {}", a);
}

But the above would compile if you instead took a copy of the integer, since clearing the vector doesn't destroy the copy.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.