Hi, recently ive been having to create runtime arrays where i have some data to iterate over and append it to an array, and although i have a way that works (and even think is the proper/accepted way)
One of my usecases: create an array of the cli arguments. Solution:
pub fn argsToArray(gpa: std.mem.Allocator, args: std.process.Args) []const []const u8 {
var result: std.ArrayList([]const u8) = .empty;
defer result.deinit(gpa);
var iter = args.iterate();
_ = iter.skip(); // skip program name
while (iter.next()) |arg| result.append(gpa, arg) catch @panic("insufficient memory");
return result.toOwnedSlice(gpa) catch @panic("insufficient memory");
}
I dont like having to use ArrayList. i dont quite like it. I think i find it “ugly”. And i feel it has unnecessary allocations. And i feel im having to create a lot of ArrayList in so many different places (even more so when there are ArrayList inside something that im also grouping in ArrayList).
TL;DR
This pattern appears again and again and again. And i dont quite actually like it and am wondering if theres another simpler way im missing, or even if not simpler, prettier.
Extra question:
- why does
args.Iteratornot have apeek()? - whats more recommended: returning
toOwnedSlice()or keeping theArrayListaround?
The extra info:
(All of this is not really relevant to the question)
The reason i had to do that instead of using args.Iterator I needed to parse the cli where some option would take a list of arguments --foo a b c --bar.
while (iter.next()) |opt| {
if (eql(u8, opt, "--foo")) {
while (iter.next()) |arg| {
if (startswith(arg, '-') break;
...
}
} else if (eql(u8, opt, "--bar") {...}
}
This is simple, and clean, and gets the values fine, but then --bar gets skipped because we already looked at it with next(). (and im realising i could have the else if be an if and it would get executed next, but that gets messy with more options and other complex parsings)
So we would need the args.Iterator to either be able to set the index back, which not possible because the iterator is consuming the arguments and only keeps remaining, or for the iterator to have a peek which it doesnt have (and i cant understand why it wouldnt, so thats another question if anyone can answer). So instead i had to do what i pasted up there: go throug it all and create my own array with all the elements. And then have my own iterator that has an index so each next increments the index and then i can if (startswith(arg, '-') {iter.index -=1; break;}.
More cases where i had to do this:
Right here, getting the list of options passed to --foo.
When i was using the args.Iterator i would need to do it with an ArrayList again.
Not anymore though. Now using my iterator that keeps the og array i can get the iter index when we enter the --foo and get the index when we get the next option that starts with - and return a slice/copy of the slice (copy of slice if i deallocate the args list after parsing, slice otherwise; havent decided yet what to do, for now i copy).
Im quite happy with this ![]()
(Actually --foo takes multiple lists so --foo a b --bar --foo c would set foo=[[a,b],[c]]), so thats another ArrayList)
Im working with sqlite data. The sqlite statement gives me an iterator. So i want to iter through the rows to get an array and then i will further work with that.
Same process.
And lastly (so far) those rows have some data in strings that i want to split into an array of strings.
mem.split... returns an iterator. Again the same.
Although im thinking that for this particular case i could do a mem.count of the splitting character and then use an `ArrayList with a fixedbuffer of that lenght. And that should remove some allocations.