I understand this might be because I'm not passing in a reference, so a copy is made inside the function. But I read somewhere that passing in reference is suboptimal in terms of performance.
My question then is why the compiler requires the "mut" key word in the function arguments? And what would be the recommended way for achieving the goal? Thanks!!
However, that doesn't pass borrow checking because you mutably-borrow v twice. There are multiple ways to work around that, but since you want the first elements the easiest way is a slice pattern:
fn main() {
let mut v: Vec<u64> = vec![1,2,3];
if let [x, y, ..] = v.as_mut_slice() { mutate_u64(x, y); }
println!("{:?}", v);
}
fn mutate_u64(x: &mut u64, y: &mut u64) {
*x += 1;
*y += 2;
}
mut keyword in fn mutate_u64(mut x: u64, mut y: u64) means that the varable x and y is mutable, you can change them in the function, if you remove the mut, you will not be able to change them. In this way you can avoid creating a "shadow" mutable variable in the fucntion.
Primitive types (except u128 and i128) are less than 8 Bytes, and a reference is indeed a 64bit pointer (on x86-64 platforms), so passing a primative type should be as efficiency as passing a reference. However, if you pass a primitive by reference, you probobly will dereference it, so theoretically *x += 1 will be slower than x += 1.
So my suggestion is that if you are using primative types and don't need reference, pass by value.
P.S. In this case, references should be used since you want to modify elements in a vector, check other answers to deal with "double mutable reference" problem.
Perhaps I was misunderstood: I agree that they are less efficient than passing by-value. However, if the OP needs a to modify them, then references will be the fastest way available, and also very fast.
Thanks! I do need to mutate the other elements than the first few, it could be arbitrary two locations from the vector. Looking at the replies it seems mrQuidome's reply is closest to what I need. Really thanks for the help!
Modern compilers will use the tech Copy elision to optimize codes, return value is a typical scenario.
To check if the code is optimized as you want, you can use the playground, and choose "LLVM IR" beside the Run button (Or cargo rustc -- --emit=llvm-ir on your machine). Use this tool for checking whenever you are doubting about the optimization.