Struct is not passed by reference when passed to a

2019-07-31 03:33发布

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?

标签: struct vala
2条回答
神经病院院长
2楼-- · 2019-07-31 03:47

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;
}
查看更多
相关推荐>>
3楼-- · 2019-07-31 03:50

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.

查看更多
登录 后发表回答