Is it ok to mutate a slice's `.len` field?

I’m working on an n-dimensional array just for fun and wanted to use fixed size arrays for shapes and strides below:

const max_dims: usize = 5;

pub fn Ndarray(comptime Dtype: type) type {
    return struct {
        buf: []Dtype,
        strides: [max_dims]usize,
        shape: [max_dims]usize,
        // other stuff omitted
    };
}

For a 2d-array arr, I’d like to set arr.shape.len = 2 so that users of Ndarrays can access the shape field directly without using an accessor.

It seems to work but I’m not sure if I’m breaking some kind of a language contract by modifying it.

The length of an array [max_dims]usize is a compile-time known constant and cannot be changed.
The code you propose here shouldn’t even compile:

error: cannot assign to constant
 arr.shape.len = 2;
 ~~~~~~~~~^~~~

Hmm, I guess I don’t actually need to modify as I can make a new slice with arr.shape[0..new_dims] :slight_smile:

Yeah, I noticed that after I had posted. I thought I had some variant of this where mutating the .len field was allowed.

Correct, and just for completeness, it’s ok to modify the len field of a slice.

2 Likes

Interesting. Is there runtime safety check on the operation?

Not sure if this would apply but keep in mind that you can also receive the dims as comptime parameters.

pub fn Ndarray(
    comptime Dtype: type,
    comptime strides_dim: usize,
    comptime shape_dim: usize,
) type {...

Not for a fixed length array as you’ve written but for a slice you totally can.

var s:[]u8 = undefined;
s.ptr = some_c_ptr;
s.len = some_len;

To be honest though its much easier in most cases I’ve found to just construct a new slice with the values you want off of another pointer. which you’re allowed to do if they’re multi-pointer [*].

var my_array_ptr: [*]u8 = ...;
var s = my_array_ptr[0..some_len];

and there is no safety checking on this. just bounds checking on the slices after you’ve constructed them

3 Likes

Thanks! I didn’t expect any safety checks for doing this either. And that seems fine.

The reason why I wanted to mutate the original slice is because I’d like the array allocation for a slice to be embedded in a struct, but I’d like to vary its length in the struct’s “constructor”. Storing another slice just to be able to vary the increases memory the struct’s memory usage.

An additional example of modifying the len field of a slice is in the zig standard library: https://github.com/ziglang/zig/blob/master/lib/std/array_list.zig

Here’s a specific line where the len field is being modified. https://github.com/ziglang/zig/blob/master/lib/std/array_list.zig#L159

2 Likes

no it’s unsafe, slicing (ie using the square bracket operations, eg foo[10..]) is the safe way of manipulating a slice

1 Like

If you do slice.len -= 1 you get a safety check in debug modes. But remember, if it’s possible for underflow to occur, you should handle that in your code, not just let Zig insert a panic and rely on that.

1 Like