I am new to the language (but not to programming), and first of all, thanks to the documentation people for their great work.
Anyway, I've been working my way through the (online) Rust Book, and I've come across a little mystery in Chapter 8.
The text gives the following example of creating a vector and accessing one of its elements:
let v = vec![1, 2, 3, 4, 5];
let third: &i32 = &v[2];
However, I have discovered that this can be done without references. The following code compiles, runs, and both versions produce the same result:
let v = vec![1, 2, 3, 4, 5];
// This works.
let third: &i32 = &v[2];
println!("The third element is: {} <1>", third);
// This works too!
let third: i32 = v[2];
println!("The third element is: {} <2>", third);
So, can someone please explain why both versions work, and why the book doesn't mention that the assignment to 'third' works without references?
The first alternative defines third to be a reference to an i32 (e.g., 8 B on a machine with 64-bit addressing) and assigns it an immutable (i.e., read-only use) reference to the third element of the vector v.
The second alternative defines third to be an i32 (4 B) and moves the value that was in v[2] into third. Afterward the vector element v[2] cannot be read.
This works because i32 implements Copy, so the values in the Vec are just copied out. Try the following to see what would happen otherwise:
#[derive(Debug)]
struct NoCopy(i32);
fn main() {
let v = vec![NoCopy(1), NoCopy(2), NoCopy(3), NoCopy(4), NoCopy(5)];
let third: NoCopy = v[2];
println!("The third element is: {:?}", third);
}
Thank you. That makes sense, but it does not appear to be entirely true. I just wrote this code, which compiles and runs:
fn vector_access_repeated() {
let v = vec![1, 2, 3, 4, 5];
let third: &i32 = &v[2];
println!("The third element is: {} <1a>", third);
let third: i32 = v[2];
println!("The third element is: {} <2a>", third);
let third: &i32 = &v[2];
println!("The third element is: {} <1b>", third);
let third: i32 = v[2];
println!("The third element is: {} <2b>", third);
}
According to your explanation, that should not work. Am I misunderstanding something?
It works for any element type that is Copy, since Rust then knows that you can still safely access both the value in the vector and the copy in your new variable.
For other element types, it won't compile at all ("cannot move out of indexed content") since it would otherwise have to invalidate the whole vector (it cannot be "moved partially").
If you added Copy (and Clone, since all types that implement Copy also have to implement Clone) to the list of derives in @skysch's example, it'd behave like your original code does
#[derive(Debug, Copy, Clone)]
struct CanCopy(i32);
fn main() {
let v = vec![CanCopy(1), CanCopy(2), CanCopy(3), CanCopy(4), CanCopy(5)];
let third: CanCopy = v[2];
println!("The third element is: {:?}", third);
}