Search code examples
genericsvala

Vala: Passing a generic array corrupts values


How can I pass an array to a generic function? The following code does compile, but the output gets somewhat corrupted:

void foo<T> (T[] arr) {
    foreach (T element in arr) {
        var element2 = (int) element;
        stdout.printf (element2.to_string() + "\n");
    }
}


void main () {
    int[] array = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    foo<int> (array);
}

Output:
0
2
4
6
8
113
0
-1521013800
0
0

What am I doing wrong?


Solution

  • You discovered a compiler bug.

    The array is treated as an array of pointers and then things go wrong when foreach tries to iterate over that pointer array.

    BTW: It works if you compile for 32-Bit, because sizeof(gint) == sizeof(gpointer) there.

    The compiler should not allow a value typed array to be passed to the generic function.

    This can be fixed by using Gee.Collection<G> instead of an array:

    void foo<T> (Gee.Collection<T> coll) {
        foreach (T element in coll) {
            var element2 = (int) element;
            stdout.printf (element2.to_string() + "\n");
        }
    }
    
    
    void main () {
        int[] array = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        foo<int> (new Gee.ArrayList<int>.wrap (array));
    }
    

    This works because Gee.Collection<G> has it's own iterator implementation which seems to be safer in this case. It's not a perfect solution either, some Gee types have problems with value type parameters, because they expect the type to be nullable.

    Generics in Vala are always a bit tricky, you should not be passing value types into generics.

    If you use an int? (nullable int, which is a reference type) it works as expected:

    void foo<T> (T[] coll) {
        foreach (T element in coll) {
            var element2 = (int?) element;
            stdout.printf (element2.to_string() + "\n");
        }
    }
    
    void main () {
        int?[] array = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
        foo (array);
    }
    

    Nullable types aren't perfect either, because they need more memory and they don't perform as good as value types, but that's just the way it is.

    The code is still a bit problematic, because you are casting the element to an int (or int? in my version), but I think you know that it's not a good idea, since this is only some test code, right?