zig
zig copied to clipboard
Add .stride field to slices
I propose that if you have a type T=struct{foo: U} and slice []T, accessing a slice with a field ([]T.foo) returns a slice []U with stride @sizeOf(T).
Example use case:
const entry = struct {
key: []const u8,
value: []u8,
}
const entrylist = std.ArrayList(entry);
fn getKeys(self: entrylist) [][]const u8 {
return self.toSlice().key; // returns slice with stride equal to @sizeOf(entry)
}
fn getKeyList(self: entrylist, allocator: *Allocator) []u8 {
return std.mem.join(allocator, ",", self.getKeys());
}
fn getValues(self: entrylist) [][]const u8 {
return self.toSlice().value; // returns slice with stride equal to @sizeOf(entry)
}
fn getValueList(self: entrylist, allocator: *Allocator) []u8 {
return std.mem.join(allocator, ",", self.getValues());
}
I think, if we do this, the type system will need to know -- or else we make every slice 3 usizes when for most applications we only need 2. Also, as written, there's a syntactic ambiguity: what if T has a field named len?
I think, if we do this, the type system will need to know -- or else we make every slice 3
usizes when for most applications we only need 2.
This is likely to dovetail nicely with #1830
Also, as written, there's a syntactic ambiguity: what if
Thas a field namedlen?
Good point! we might need some sort of other indexing operation (or helper) on slices.
The base proposal of adding a .stride field still stands
Is there any point in having a slice/array type which has a comptime-determined stride (analogous to how there is a type for arrays with a comptime-determined length)?
this feels like it'd be very unintuitive to new users and i think this and more use cases could be catered to with iterators. wanting the native support in a for loop is definitely an appeal i understand
The thing I like most about this proposal is the potential for having ArrayList and MultiArrayList even more interchangeable. If you decide to change your data layout, you shouldn't need to rewrite all of your code. This would allow for easier switching between ArrayList and MultiArrayList going forward. In my opinion, stride should work on the relevant types in similar-looking way to how align looks in status quo.
const Entry = struct {
key: []const u8,
value: u64,
}
fn getKeys(self: std.ArrayList(Entry)) stride(@sizeOf(Entry)) [][]const u8 {
return self.items[.key];
}
fn getValues(self: std.ArrayList(Entry)) stride(@sizeOf(Entry)) []u64 {
return self.items[.value];
}
fn getKeyList(allocator: Allocator, self: std.ArrayList(Entry)) ![]u8 {
return std.mem.join(allocator, ",", self.getKeys());
}
fn getValueList(allocator: Allocator, self: std.ArrayList(Entry)) ![]u8 {
return std.mem.join(allocator, ",", self.getValues());
}
I'm also thinking about strided slices like I use with python and numpy for numerical work
e.g. x = np.arange(10)[2:8:2] or data[start:end:stride].
In zig could this be achieved by a data[start..end..stride] ?
And maybe even like the data[start..] where no end is specified, data[start....stride] would still work. Though the python equivalent data[start::stride] with 2 colons reads a bit cleaner than the 4 dots