(Reposted from http://www.reddit.com/r/rust/comments/2oqtei)
Lets say I have some struct for 3D a geometric vector and some operations defined on this as follows:
struct Vec3 {
pub x : f64,
pub y : f64,
pub z : f64,
}
impl Vec3 {
fn dot(&self, rhs : &Vec3) -> f64 {
self.x * rhs.x + self.y * rhs.y + self.z * rhs.z
}
}
impl Sub<Vec3, Vec3> for Vec3 {
fn sub(&self, rhs : &Vec3) -> Vec3 {
Vec3 {x:self.x - rhs.x, y:self.y - rhs.y, z:self.z - rhs.z}
}
}
I can do some calculations with this like so:
fn some_vector_calculation(v1: &Vec3, v2: &Vec3, v3: &Vec3) -> f64 {
v1.dot(&(*v3 - *v2))
}
fn main() {
let v1 = Vec3 {x:1., y:2., z:3.};
let v2 = Vec3 {x:4., y:5., z:6.};
let v3 = Vec3 {x:7., y:8., z:9.};
println!("result={}", some_vector_calculation(&v1, &v2, &v3));
}
But what I’d like to be able to do is simplify some function calls and have this still work:
fn some_vector_calculation(v1: &Vec3, v2: &Vec3, v3: &Vec3) -> f64 {
v1.dot(v3 - v2) // doesn't compile
}
fn main() {
let v1 = Vec3 {x:1., y:2., z:3.};
let v2 = Vec3 {x:4., y:5., z:6.};
let v3 = Vec3 {x:7., y:8., z:9.};
println!("result={}", some_vector_calculation(v1, v2, v3)); // doesn't compile
}
The point is that I don’t think that calling code should have to distinguish between function parameters passed by value and function parameters passed by simple immutable reference (with lifetime that doesn’t exit from the called functions scope) at the point of function call.
It’s not such a problem to write those stars and ampersands in the example code above, in order to get it to work, but I’m finding that when working with more ‘real’ code this can actually be quite a significant irritation and causes (I think) unnecessary development friction and code complexity.
I think that the distinction between pass by value and (simple) pass by reference is better considered as an implementation detail, which is essentially just defined within the scope of the called function.
Yes, this parameter passing behaviour can have performance implications, and so you could argue that you want to see this explicitly at the point of call for this reason, but I really think this kind of performance issue is better considered the responsibility of the called function (just as with other performance implications relating to the called function), and I don’t think that the ability to see whether or not a parameter is being passed by value or reference explicitly at call site is worth all the extra hassle that comes with this.
Importantly, if I want to change some function calling signature from pass by value to pass by reference (or vice versa) I don’t want to also have to go back to all the code that calls that function and update all this stuff for the change.
Note that this is like pass by value or pass by const reference in C++. In that case, of course, there is not the whole notion of borrowing and borrow checking, but in the specific (probably quite common) case where the borrow lifetime doesn’t exceed the called function it seems that this is essentially exactly the same thing, and for people used to programming in C++ I think that having to change the function call site according to which of these two parameter passing methods is chosen by the function will probably seem very clunky and annoying (as it does for me).
I don’t know anything about the implementation details of Rust, or what kind of actual concrete changes would need to be made to the language in order to change this function call behaviour, just speaking as a potential user of the language really at this point. (But I somehow really like what I have seen of Rust so far, and could be interested in getting more involved, if that could help to change stuff like this!)