zig icon indicating copy to clipboard operation
zig copied to clipboard

Add .stride field to slices

Open daurnimator opened this issue 6 years ago • 6 comments
trafficstars

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());
}

daurnimator avatar Apr 11 '19 10:04 daurnimator

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?

ghost avatar Nov 30 '20 11:11 ghost

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 T has a field named len?

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

daurnimator avatar May 13 '21 01:05 daurnimator

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)?

lunacookies avatar Feb 18 '23 09:02 lunacookies

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

nektro avatar Feb 19 '23 00:02 nektro

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());
}

Validark avatar Jun 08 '23 21:06 Validark

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

digitalsignalperson avatar Aug 09 '24 21:08 digitalsignalperson