struct Data {
public int x;
}
void change_x(Data data) {
data.x = 123;
}
Data a = Data();
change_x(a);
print("%d", a.x); // 0
but the document says:
when a struct type instance is passed to a method, a copy is not made. Instead a reference to the instance is passed.
- in https://wiki.gnome.org/Projects/Vala/Manual/Types
What is wrong?
I think the quoted text you are refering to is either outdated or was wrong to begin with.
You have to use ref
(or out
) if you want it to be passed by reference (hence the name ref
).
struct Data {
public int x;
}
void change_x (ref Data data) {
data.x = 123;
}
int main () {
Data a = Data ();
change_x (ref a);
print ("%d\n", a.x);
return 0;
}
Structs in Vala are implemented as copy on assignment and pass by reference. So you can think of your example as copying the struct because it is being assigned to a parameter in a function and then that copy is passed by reference. This is what is happening behind the scenes in the generated C code, but from the Vala side it means a struct is a value type. Only when interfacing with a C library is it useful to know a copy of the struct is passed by reference. The quote from the manual refers to struct methods, but before we look at that in detail let's understand a bit more about value and reference types.
Vala, like Java, C# and many other languages, has two kinds of data types: value types and reference types.
Value Types are Passed by Value
When a value type is passed as an argument to a function or a method then the value is passed as the argument, but it is a copy of the value. If the function or method goes on to modify the parameter it received this will not change the value in the calling code. The code is encapsulated.
The following example:
void main () {
int a = 23;
print ("Initial value: %i\n", a);
modify_example (a);
print ("Final value: %i\n", a);
}
void modify_example (int x) {
x += 100;
}
produces:
Initial value: 23
Final value: 23
Although the value is modified in the function it does not also modify the value from the calling code.
Value Types Can be Passed by Reference
Instead of passing the value to a function or a method the use of the ref
keyword will pass a reference to the value. This creates an alias for the calling value. The result is two identifiers for the same memory location.
By just adding the ref
keyword the following example:
void main () {
int a = 23;
print ("Initial value: %i\n", a);
modify_example (ref a);
print ("Final value: %i\n", a);
}
void modify_example (ref int x) {
x += 100;
}
now produces:
Initial value: 23
Final value: 123
By calling modify_example ()
the side effect is to also change the value in the calling code. The use of ref
makes this explicit and can be used as a way for a function to return multiple values, but in this example it would be clearer to return
the modified value instead of passing by reference.
Reference Types are Always Passed by Reference
Objects are reference types. This example doesn't use an explicit ref
, but the value is changed in the calling code:
void main () {
var a = new ExampleReferenceType (23);
print ("Initial value: %i\n", a.value);
modify_example (a);
print ("Final value: %i\n", a.value);
}
class ExampleReferenceType {
public int value;
public ExampleReferenceType (int default = 0) {
this.value = default;
}
}
void modify_example (ExampleReferenceType x) {
x.value += 100;
}
This produces:
Initial value: 23
Final value: 123
Objects modified in this way can cause problems when tracking down a bug. That is an advantages of making value objects immutable. This would be done by only setting the value in the constructor, making all fields private and only using a property to get the value, but not set it.
Structs as Value Types
The following code:
void main () {
ExampleStruct a = { 23 };
print ("Initial value: %i\n", a.value);
modify_example (a);
print ("Final value: %i\n", a.value);
}
private struct ExampleStruct {
public int value;
}
void modify_example (ExampleStruct x) {
x.value += 100;
}
is similar to your code and produces:
Initial value: 23
Final value: 23
This is the same behaviour in Vala as the other value types, but if you look at the C code by using the --ccode
switch with valac
you will see the struct is copied and passed by reference. This is relevant when you need to understand how Vala maintains the C ABI (Application Binary Interface).
Struct Methods
The reference you make to the manual may not be clear, but I think it relates to methods within structs. If modify_example
is moved inside the struct definition:
void main () {
ExampleStruct a = { 23 };
print ("Initial value: %i\n", a.value);
a.modify_example ();
print ("Final value: %i\n", a.value);
}
private struct ExampleStruct {
public int value;
public void modify_example () {
this.value += 100;
}
}
this now produces:
Initial value: 23
Final value: 123
The method now operates on the instance.
[SimpleType] Structs
The section from the manual you quote also states in the next sentence:
This behaviour can be changed by declaring the struct to be a simple
type.
So for completeness here is the final example:
void main () {
ExampleStruct a = { 23 };
print ("Initial value: %i\n", a.value);
a.modify_example ();
print ("Final value: %i\n", a.value);
}
[SimpleType]
private struct ExampleStruct {
public int value;
public void modify_example () {
this.value += 100;
}
}
and this produces:
Initial value: 23
Final value: 23
Although the method is still defined within the struct the instance is passed by value instead of reference.
Simple type structs are how the fundamental value types are defined in Vala, for example int
and int64
. If you want a struct method defined that acts on a simple type struct instance then the method will have to be defined to return a new instance containing the modified value.
Conclusion
Structs are value types in Vala, but it is useful to know how they are implemented to understand compatibility with the C ABI. In your example the struct behaves as a value type, but is copied and passed by reference in terms of the C ABI. The quote seems most relevant to methods defined within structs.