(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!)