My PinePhone OS got some basic Apps
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
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
is not indexable and not a range
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