Slightly confused by slices - where is the length?

I’m working through the Ziglings, and I’m finally up to allocators (ex. 96). The exercise shows how to allocate a slice. The solution is:

    // allocate memory for this array
    const avg: []f64 = try allocator.alloc(f64, arr.len);

I was confused by this, because I don’t understand where the slice’s length is stored? I assumed a slice would be a sort of smart-pointerish struct with a length and a raw pointer. And then the allocation would be for the buffer, and the struct might be stored on the stack.

That’s pretty much it. A slice in Zig is like a struct defined as follows:

const Slice = struct {
    ptr: [*]T,
    len: usize,
};

So ptr is a many-item pointer and the length is just a usize. But at the syntax level, you deal with this as []T and Zig provides the syntax sugar to perform operations like indexing and slicing. But under the hood it’s pretrty much what you think. If you do a @TypeOf(avg.ptr) and @TypeOf(avg.len) you can peek under the hood.

Hey, and welcome to Ziggit!

3 Likes

Thanks! So I guess my confusion is: if the slice really is a struct, how does it makes sense to assign the result of allocator.alloc, (presumably a pointer a buffer with exactly enough room for the floats) directly to avg, a struct, as opposed to assigning to avg.ptr?

In Zig, allocator.alloc always returns a slice (on success) so you could say that alloc allocates space, creates the slice with its ptr field pointing to this allocation, sets the len field to the length, and returns that slice to be assigned to avg. This is a step forward versus C’s malloc for example that just returns the pointer as you thought.

4 Likes

Ahhh. Gotcha. Thank you!

1 Like

One other “trick” that makes it work to return and assign the whole struct is that Zig will generate the instructions needed to copy the whole struct into the destination variable’s space. So it’s possible to return a struct even though it looks like returning stack space.