I have a pointer to a field of a struct: data: *T, however it doesn’t actually point to the field, because the field is optional: field: ?T. I can’t use @fieldParentPtrbecause data needs to be of type *?T which it isn’t. I can’t just cast data, as the pointer might not point to the correct location, or at least that’s what I think.
Is there around this, or do I have to go redesign some stuff?
The field doesn’t need to be a pointer. The object you call @fieldParentPtr("name", object) does. Both data: *T and data: ?T would be valid targets. If I understand what you mean. This should work.
Now that I’ve written it, I’m less convinced, it’s bug free (I do depend a lot on the compiler ). You might need parent to be parent(*const ?T) You should test instead of trusting me. But the docs don’t mean that the type of the field needs to be a pointer. I.e. *T and T are both valid. But that you need a pointer to the type, that is the field. So **T and *T respectively. @fieldParentPtr works for any type, but it’s just the offset of the address.
Another example
const T2 = struct{
base: usize,
};
test "example" {
const t2: T2 = .{ .base = 2 };
// notice t2.base isn't a ptr, but &t2.base is a pointer to the field.
try std.testing.expectEqual(&T2, @as(*T2, @fieldParentPtr(&t2.base));
// They're equal here, because the offset of the first field is 0
// ptr + 0 == ptr;
}
Here you pass a pointer to the data field of the optional value data1, not the pointer to the entire field. And since data1 is of type ?T the type @fieldParentPtr expects is *const ?T, and the compiler gives an error.
Now this was a very easy fix, I just had to pass in the bigger Data struct, instead of the ChildData, but even though I have solved the problem, I am still wondering if it’s possible to use @fieldParentPtr on a field that has been unwrapped.
You can try, but that behavior isn’t offered by the compiler… That said, @offsetOf() exists, so if you’re feeling really brave. There’s nothing stopping you from doing the ptr math yourself. But, in the case where you can’t pass ?T because you know it’s unwrapped. For all the cases I can imagine, you also can’t prove the pointer is what you expect. IMO, your time and attention would be better spent writing code that makes @fieldParentPtr() easy to use, by passing *?T instead of the unwrapped ?T… if you did want to use it. e.g. for t: *?T use if (t.* == null) return error.Null; and then t.? throughout.
as you also cannot use @offsetOf to extract the offset of an optional’s payload.
which seems to contradict my suggestion. I knew the pointer math would be sketchy, (and I still assume there’s a dangerous, hacky way that’s nearly guaranteed to break with every zig version) But it’s clearly not going to be as easy as adding 8 to the offset.
optionals have no guaranteed layout, and there are proposals to do more optimization on the representation, so any tricy dependency you take on the current implementation of an optional in memory is very likely to break.