Parameter Types
In Asylum, there are different ways to pass data to functions. In the previous section, we explored how to create functions that take parameters and return a result that utilized those parameters. However, consider the below code:
Warning
This code will not compile:
fn setValue(int val) {
val = 7; // Error, can not set value of an input parameter!
}
The compiler sees that val
is an int
argument in the function. However, we have not told the compiler that we want to be able to modify the value, so it will not let us! In order to tell the compiler that, there are different parameter types.
Type Listing
Type |
Example |
Explanation |
---|---|---|
In |
|
Parameter |
Ref |
|
Parameter |
Move |
|
Parameter |
Note
The parameter types in
and ref
are around the same performance wise!
More advanced programmers may be concerned about passing by value rather than passing by reference. In other languages, passing by value usually means that the entire structure is copied when calling the function, which is usually not desired.
However, in
parameters in Asylum automatically pass a reference to a constant (like const MyStruct&
in C++) rather than copy the structure (pass by value) when it is less costly to do so. Asylum was designed to have the common and most intuitive case be the most efficient! If you are confused, do not worry, this is not too important. Just know that using ref
is not more or less efficent.
Reference Parameters
The first solution we may do if we want to modify the in
value is copy it to another variable:
fn setValue(int val) {
int newVal = val;
newVal = 7; // We can modify this!
}
fn main() {
int x = 3;
println(x);
setValue(x);
println(x);
}
Output:
3
3
Though do note that this is not what we wanted. While we can modify the value passed if we copy it into another variable, it does not impact the original value! This is solved by trailing the parameter type with &
:
fn setValue1(int& val) {
val = 7; // We are allowed to do this now!
}
fn setValue2(int& val) {
val = 5;
}
fn main() {
int x = 3;
println(x);
setValue1(x&);
setValue2(x&);
println(x);
}
Output:
5
Note that we have to do x&
to tell the compiler we want to pass a reference to the variable rather than pass it as an in
parameter. This is because functions with different parameter types are different functions! This means the following code is perfectly valid:
fn myFunc(int a) -> int => a;
fn myFunc(int& a) -> int => a;
fn myFunc(int% a) -> int => a;
fn main() {
int x = 3;
println(myFunc(x));
println(myFunc(x&));
println(myFunc(3%)); // Appending % after a variable or value moves it.
}
Output:
3
3
3
Move Parameters
You now hopefully understand how both in
(default) and ref
(reference) parameters work. But what is this mysterious move
? Sometimes we have structures we can not copy, or are very large and do not want to be copied. However, we would still like to use the structure somewhere else. This is done by “moving” it to the function we are calling:
struct Image {
pub:
Pixels pixels;
}
impl Image {
pub fn setPixels(Pixels% pixels) {
this.pixels := pixels;
}
}
In the above example, Pixels
is a structure that contains large resources and thus should be copied or constructed as little as possible. Therefore, we want to move pixels to the image rather than copy them. This is done by using the move operator :=
. However, we are only allowed to move an item we have ownership over. If pixels
was passed as in
or as ref
, then we would only get to observe the structure rather than own it! We’re allowed to make copies of these items, but we can not move them!
Do not worry if this is confusing, Asylum’s “move semantics” (or moving memory rather than referencing or copying) will be discussed in its own section showing how to use it in practice and go into further detail.
Use After Move
TODO!!!