Brand new ZIg user trying to port 3K line science app w/100 pointers in a struct

Hi,

For a few hours, I have been fighting to declare pointers in a struct correctly, trying several approaches. I’ve tried “[ ]T” and “[*]T” for the declarations, but never any luck. I’ve attached a portion of my application that compiles with an error.

Thanks in advance.

PS I can likely have a first cut ported and posted to github by tomorrow if I can just get the data structure set up.

z.zig (12.8 KB)

In terms of an immediate fix, your code basically works, you’re just not handling the possible errors from your allocator.create calls. Adding try to all of them allows your example to compile successfully.


However, in terms of overall feedback:

  • Note that using allocator.create to create arrays only works here because domElems/edgeNodes/etc are comptime-known. If they were runtime known instead, you would want to use allocator.alloc rather than allocator.create.
  • If you haven’t already, I highly recommend familiarizing yourself with the many different pointer types available in Zig. IMO, many-item pointers to an unknown number of items [*]T should basically be treated as a pointer-of-last-resort (that is, any other pointer type is probably better in most situations). It’s not as clear cut in your case since you’re allocating so many different arrays of similar lengths, so storing slices (a pointer + a length) would increase the size of your struct, but it’s worth at least knowing what options you have.
5 Likes

Thanks. SInce everything is known at compile time for this example, I was hoping to re-jigger this somehow to not use an allocator at all. Unfortunately, I just looked at Zig for the first time today, so getting my head around how to achieve that probably won’t jump out at me for a few days.

In that case, you wouldn’t use pointer fields, and instead put the arrays directly in the struct:

const edgeElems: Index_t = 20;
const edgeNodes: Index_t = edgeElems + 1;
const domElems: Index_t = edgeElems * edgeElems * edgeElems;

const Domain = struct {
    nodelist: [domElems * 8]Index_t,
    lxim: [domElems]Index_t,
    // ...
};

(the struct might end up being quite large, though)

The equivalent initialization would look like this:

const domain = Domain{
    .nodelist = undefined,
    .lxim = undefined,
    // ...
};

Note: it is important to understand what undefined means in Zig

3 Likes

Thanks again.

It’s not as clear cut in your case since you’re allocating so many different arrays of similar lengths, so storing slices (a pointer + a length) would increase the size of your struct, but it’s worth at least knowing what options you have.

I’m hoping this example may help me to get the ear or one of the core developers at one point. There is a very powerful pointer-free way of dealing with data that also enhances parallelism and reduces errors. Memory errors become effectively impossible with what I have to propose.

New language proposals are not currently being accepted.

There may be an existing proposal that’s relevant, but I’d recommend getting some experience with the language before jumping into the language proposal side of things.

3 Likes

Thanks about the undefined comment. I will look at it more closely. I initially thought my struct declaration could have fields like this:

   letap     : []Index_t = undefined,

and then at instantiation time I could do this:

      .letap = [domElems]Index_t,

but it didn’t work. That would have felt “natural” to me, because then different instantiations could specialize with different internal structures. The object file could potentially contain information concerning this that could be detangled/verified at link-time, but that would be a lot of effort to implement, which may be why it was not implemented that way.

Update: Actually upon re-reading what you posted, it looks like what I originally tried should have worked. I was working through a lot of things at that point, so maybe I was tripping over a different bug, rather than the data structure implementation, and I gave up too early.

letap: []Index_t = undefined, is setting the field’s default value to undefined, which means by default it will be a slice with arbitrary/invalid ptr and len values (that is, the pointer itself will have an invalid value). In general, using undefined as a default value in a struct is inadvisable.

.letap = [domElems]Index_t, is attempting to set the value of the letap field to a type. If you wanted to make a slice point to an array, you’d need to get a pointer to the array (which can coerce to a slice type automatically):

var letap_buf: [domElems]Index_t = undefined;

var domains = Domains{
    .letap = &letap_buf, // effectively equivalent to letap_buf[0..letap_buf.len]
    // ...
}
3 Likes