I’m abusing the keys of an AutoHashMap
to act as a Set (I know that this exists, but I’m trying to solve AoC after-the-fact without any dependencies in an attempt to learn the language).
I first tried this:
const std = @import("std");
const print = std.debug.print;
const expect = @import("std").testing.expect;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var first_set = std.AutoHashMap(u32, bool).init(allocator);
var second_set = std.AutoHashMap(u32, bool).init(allocator);
defer first_set.deinit();
defer second_set.deinit();
first_set.put(42, true) catch unreachable;
first_set.put(75, true) catch unreachable;
first_set.put(180, true) catch unreachable;
var first_set_key_iter = first_set.keyIterator();
while (first_set_key_iter.next()) |k| {
_ = first_set.remove(k.*); // `k.*` necessary because `k` is a pointer, but `.remove()` takes a value
second_set.put(k.*, true) catch unreachable; // Ditto above
}
try expect(second_set.contains(42));
try expect(second_set.contains(75));
try expect(second_set.contains(180));
try expect(first_set.count() == 0);
}
But the .contains
tests failed. By adding some debug logging statements, I found that the values being added to second_set
were completely different than the values being removes from first_set
, even though they were both k.*
.
I assume that this is something to do with the fact that k
was derived by iterating over first_set
, and that first_set.remove
changes the set, but I don’t see how that would have any effect - k
is a *u32
(i.e. a pointer to a u32
value), so once that value has been retrieved (by going “via” first_set
) I don’t see why changes to first_set
would affect its value.
That is, the way I (wrongly) imagine while (first_set_key_iter.next()) |k| {
working is:
- Find the next key of
first_set
- Identify what
u32
it is - Create a pointer to that
u32
value - Set
k
equal to that pointer
Under this (again, wrong) paradigm, modifications to first_set
wouldn’t affect the value of k.*
- after step 2, the original source of k
is irrelevant. Even though (say) 42
was originally the answer to “what is the first key of first_set
?”, no modification to first_set
will change the fact that k
is “a pointer to 42
”.
So, my first question: What’s an alternative paradigm that more-closely matches the actual operation? Is it closer to “k
is a pointer to the-u32-which-is-the-first-key-of-first_set
”? What are some use-cases that that makes easier, smoother, or faster?
And my second question is - what’s a more idiomatic way to achieve what I’ve done above? The code below works, but I suspect there’s a better way:
...
var first_set_key_iter = first_set.keyIterator();
while (first_set_key_iter.next()) |k| {
const persisted_value = k.*; // _this_ will keep its value, even if `first_set` changes
_ = first_set.remove(persisted_value);
second_set.put(persisted_value, true) catch unreachable;
}
...