#How to mutate the value returned from an iterator?

1 messages · Page 1 of 1 (latest)

solar glacier
#

New zig user here, using zig version 0.10.1.

I'm trying to create a parser that parse from a buffer and return a stream of items, something like below:

const buffer: []const u8 = ... // load from file
var parser = Parser.init(allocator, buffer);
var iterator = parser.parse();

while (iterator.next()) |item| {
  // do something

  // ! deinit the item as the item is no longer needed
}

the iterator returns an item which has additional mem allocated. I'd like to deinit it as I iterate through the result. Zig complains that the item is a const pointer, so I tried to change it to |*item|, but it doesn't work, still complains the same error.

I found a workaround but it looks awkward:

var i = iterator.next();
while (i != null) {
  if (i) |*item| {
    // do something
    item.deinit();
  }
  i = iterator.next();
}

So if the |*item| syntax works for the if statement, should the syntax |*item| also work in the while loop? Is there a better way to achieve this?

past knot
#

in order for this to work, iterator.next() would need to return a pointer. then you should be able to do

while(i.next()) |item| {
  // stuff
  i.deinit();
}
#

but if next() returns something by value, and needs to be deinit(), perhaps deinit() should accept a by-value param?

#

not sure, would need more context.

#

one thing to note, you might be tempted to use a by-ref capture. but this would not be safe if next() returns by-value

while(i.next()) |*item| {
  // stuff
  i.deinit();
}
#

the compiler should reject this

solar glacier
#

yes, a by-value deinit would work. Is there an explanation of the diff between by-value vs 'by-pointer' deinit? I cannot find any good reference discussing their semantics and difference when used in the self function param. My understanding how it works could be wrong, but my guess is if the compiler sees a 'by-pointer' definition, it passes a pointer to the current struct in when calling the function, otherwise it just calls the function and uses the by-value semantics. If my understanding is correct and deinit doesn't have any special behavior, then I can't think about a case when it is needed for the self value to be a pointer in the deinit function, but I read a couple of the std lib implementation and they all use by-pointer deinit. I assume zig makes the optimization to pass by-ref when using by-value if the struct is large so there shouldn't be any significant perf difference. I chose to use 'by-pointer' because I felt that the deinit should be called on the current object, not a copied object, but changing it to be by-value should also work, because the resource need to be deallocated is pointing to the same memory when the item is copied by-value.

it looks like the compiler accept thes |*item| syntax in the while statement, but treated as if it is |item|, not sure if it's by-design or a bug.

past knot
#

seems like your intuition is pretty close to me.

#

i tend to program defensively by defaulting to const variables and by-value params.