Array of struct handling

My PinePhone OS got some basic Apps :slight_smile: and I try to simulate an interface to treat them all in the same way. So I now have an array of App structs with common interface fn pointers, and a bit of state, and use it something like below.

var apps:[MAX_APPS]App = undefined;

pub fn appTouched() void {
    var statusBar = apps[STATUS_BAR];
    statusBar.touched(x, y);
    statusBar.needsRepaint = true;
    statusBar.render();
}

Function call worked, but it turns out I was assigning to a copy? and needed to change statusBar to a pointer:

    var statusBar = &apps[STATUS_BAR];

but I was surprised that some magic-dust happens that I dont need to dereference the pointer statusBar.*.needsRepaint

I think Iโ€™m getting them mixed up with Golang struct or Java class. Are they more like a primative, ie pass by value? and that is why we can return them from functions?

I also just tripped over this:

pub fn tick() void {
  for (apps) |app| {
      app.frameCounter = frameNo;
      app.tick();
  }
}

The fn call works, but the assignment says app is constant, is there a way to change that type on the loop please? Seems like Iโ€™m maybe iterating over a copy?

12k LoC and suddenly it feels like I am back to basics :slight_smile: Thanks.

Yes, Zig will *automagically* put in the dereferencing for you. It recognizes you are using a pointer and follows it correctly. So your fix is correct. Getting the &apps[STATUS_BAR]; will get a pointer to the actual item instead of copying it. The rest of the code stays the same because Zig will handle the dereferencing for you.

For loops allow you to capture a pointer to it:

pub fn tick() void {
  for (apps) |*app| { // Notice the `*` here
      app.frameCounter = frameNo;
      app.tick();
  }
}

This informs zig that you want a mutable pointer to the app. That should update the frameCounter correctly using the same magic as before.

1 Like

Thanks, it was almost there, but needed an &:

for (&apps) |*app| {

However it still didnt work as expected, since it iterates over the undefined values and crashes. taking .len was the same. I ended up with the following where nextSlot is just the next avail array slot.

    for (0..nextSlot) |i| {
        var app = &apps[i];
1 Like

Why not use the following:

    for (&apps[0..nextSlot]) |*app| {
        _ = &app;
    }
2 Likes

You probably want to slice it before sending it through the for loop:

for (apps[0..nextSlot]) |*app| {

}
1 Like

And there it seems it doesnt need the &, cant win :slight_smile: is not indexable and not a range

OK, that works, thanks :slight_smile:

Thereโ€™s a method to the madness: Zig will do one dereference for you, but not two. Slicing makes a pointer, so does &, so slicing and & makes two.

test "array, slice, pointer" {
    var threes: [8]u8 = .{3} ** 8;
    var t_len = threes.len;
    _ = &t_len;  // this is all to trick comptime
    const sliced = threes[0 .. t_len % 10];
    for (sliced) |*b| {
        b.* += b.*;
    }
    for (&threes) |*b| {
        b.* -= 3;
    }
    // error: pointer capture of non pointer type '[8]u8'
    // for (threes) |*b| {
    //     _ = b;
    // }
    for (sliced[0..][0..sliced.len]) |*b| {
        try expectEqual(b.*, 3);
    }
    // error: type '*const []u8' is not indexable and not a range
    // for (&sliced[0..][0..sliced.len]) |*b| {
    //     try expectEqual(b.*, 3);
    // }
}
1 Like