mun
mun copied to clipboard
Pass argument by reference
Similar to C#, we want to use the ref keyword in function signatures and function calls to signal that we are passing an argument by reference.
NOTE: Do not confuse the concept of passing by reference with the concept of reference types (i.e.
struct(gc). The two concepts are not the same. A function parameter can be modified byrefregardless of whether it is a value type or a reference type.
To use a ref parameter, both the method definition and the calling method must explicitly use the ref keyword, as shown in the following example:
// `ref` implies that the arg is mutable
fn foo(ref arg: u32) {
arg += 2;
}
fn main() {
// A variable needs to be mutable (`mut`) in order to bind a `ref` to it
let mut value = 4;
foo(ref value);
assert_eq!(value, 6);
}
As opposed to C# we will not introduce the out keyword, as people can already return anonymous tuples from a function.
[discussion 1] Is there a use case for using ref without actually editing the value it refers to? If so, we should add ref mut?
[discussion 2] If we do demand ref mut, should the function call site also use ref mut?
Edit: updated the proposal example to remove ref mut. After discussion with @baszalmstra, we don't think there are any use cases where you would not want to edit the ref binding.
[discussion 2] Writing mut ref has the added benefit that you are self documenting a mutable reference at the call site.
Just to add my 2 cents: There are definitely cases where passing by reference do not imply mutation, like iterators or any aggregation;
p.s. why ref instead of Rust syntax (&) ?
I think it's ref as in a reference to the variable itself rather than a reference type.
The reason why we put ref in front of the variable instead of doing &u32, is because the ref does not apply to the value arg, but to the binding of arg. It works the same as Rust's ref pattern.
fn modify_something(ref a: i32) {
a = 7;
modify_something_else(mut a);
}
fn modify_something_else(mut a: i32) {
a = 9
}
fn example() {
let mut a = 4;
modify_something(ref a);
// The value of `a` is 7
}
Mun works differently compared to Rust, in that we have garbage collected structs (i.e struct Implicit or struct(gc) Explicit). Using the ref pattern allows us to modify the binding of a struct. E.g.:
struct(gc) Foo {
a: u32,
b: f32,
}
fn main() {
let mut foo = Foo { a: 5, b: -3.14 };
edit_foo_value(foo);
assert_eq!(foo.a, 5);
assert_eq!(foo.b, -3.14);
edit_foo_ref(ref foo);
assert_eq!(foo.a, 1);
assert_eq!(foo.b, 6.28);
}
fn edit_foo_value(arg: Foo) {
// Assigns a new GC pointer to the local variable `arg`
// This will be garbage collected after returning from this function, as it is no longer used
arg = Foo { a: 1, b: 6.28 }
}
fn edit_foo(ref arg: Foo) {
// The external `arg` binding now points to a new GC pointer
arg = Foo { a: 1, b: 6.28 }
}
Similarly, for struct(value) - which are stack-allocated - we can now modify the argument that we bind to:
struct(value) Foo {
a: u32,
b: f32,
}
fn main() {
let mut foo = Foo { a: 5, b: -3.14 };
edit_foo_value(foo);
assert_eq!(foo.a, 5);
assert_eq!(foo.b, -3.14);
edit_foo_ref(ref foo);
assert_eq!(foo.a, 1);
assert_eq!(foo.b, 6.28);
}
fn edit_foo_value(arg: Foo) {
arg.a = 1;
arg.b = 6.28;
}
fn edit_foo_ref(ref arg: Foo) {
arg.a = 1;
arg.b = 6.28;
}