#How can I correctly obtain the list of keys from a map?

1 messages · Page 1 of 1 (latest)

north sonnet
#

Here's my code. I need to implement a method called get_map_keys to get the key list of a map. I'm not sure if I need to pass an allocator and how to implement it internally.

eg:

const std = @import("std");
const Section = struct {
  info:[]const u8 = undefined
};
pub fn main() !void {
     var gpa = std.heap.GeneralPurposeAllocator(.{}){};
     const allocator = gpa.allocator();
     
     var _map = std.StringHashMap(Section).init(allocator);
     try _map.put("foo", .{ .info= "foo" });
     try _map.put("bar", .{ .info= "bar" });
 
     std.debug.print("_map.len = {any}\n", .{ _map.count() });
     std.debug.print("_map.keys = {s}\n", .{ try get_map_keys(_map) });
}

// TODO: How to correctly obtain the key and value list?
fn get_map_keys(_map: std.StringHashMap(Section)) ![][]const u8 {
    _ = _map;
    return &[_][]const u8{};
}
gritty crow
#

note that StringArrayHashMap provides a slice of keys directly

distant meteor
#

the keys are stored in _map.keyIterator().items iirc

north sonnet
gritty crow
distant meteor
#

HashMap doesnt have a .keys()?

gritty crow
#

oh, keyIterator().items is not contiguous

distant meteor
#

it isnt

#

StringArrayHashMap would be...

#

@north sonnet to clarify, if you (for whatever reason) need to keep the order of the entires, use StringArrayHashMap.

gritty crow
#

Doing it with StringHashMap instead of StringArrayHashMap looks like:

fn get_map_keys(allocator: std.mem.Allocator, map: std.StringHashMap(Section)) ![][]const u8 {
    const result = try allocator.alloc([]const u8, map.count());
    errdefer allocator.free(result);
    var key_it = map.keyIterator();
    var i: usize = 0;
    while (key_it.next()) |key_ptr| {
        result[i] = key_ptr.*;
        i += 1;
    }
    return result;
}
#

StringArrayHashMap is just return map.keys(); or return allocator.dupe([]const u8, map.keys()); depending on whether you need a clone

surreal thicket
#

@gritty crow I tried using


fn get_map_keys(allocator: std.mem.Allocator, map: std.StringHashMap(void)) ![][]const u8 {
    const result = try allocator.alloc([]const u8, map.count());
    errdefer allocator.free(result);
    var key_it = map.keyIterator();
    var i: usize = 0;
    while (key_it.next()) |key_ptr| {
        result[i] = key_ptr.*;
        i += 1;
    }
    return result;
}

and I'm getting an integer overflow (Zig 0.11.0)

thread 22386 panic: integer overflow
/home/dan/Install/zig-linux-x86_64-0.11.0/lib/std/hash_map.zig:1146:42: 0x2a6d35 in containsAdapted__anon_10861 (lvm)
            const mask = self.capacity() - 1;
                                         ^
/home/dan/Install/zig-linux-x86_64-0.11.0/lib/std/hash_map.zig:1402:40: 0x299f10 in containsContext (lvm)
            return self.containsAdapted(key, ctx);
                                       ^
/home/dan/Install/zig-linux-x86_64-0.11.0/lib/std/hash_map.zig:624:50: 0x28c654 in contains (lvm)
            return self.unmanaged.containsContext(key, self.ctx);
                                                 ^
/home/dan/proj/lvm/src/runtime/Naive.zig:200:30: 0x279c10 in _substitute (lvm)
            if (vars.contains(param)) {
                             ^
/home/dan/proj/lvm/src/runtime/Naive.zig:155:44: 0x244790 in substitute (lvm)
    const result_idx = try self._substitute(gpa, ast, vars.*, node_idx, ident, arg_idx);
                                           ^
/home/dan/proj/lvm/src/runtime/Naive.zig:50:63: 0x237706 in reduce (lvm)
                        const result_idx = try self.substitute(gpa, ast, app_left.abstraction.rhs, abs_var.ident, node.application.rhs);
                                                              ^
/home/dan/proj/lvm/src/main.zig:89:48: 0x236ac7 in repl (lvm)
        const result = try naive_runtime.reduce(allocator, &ast, root_node_idx);
                                               ^
/home/dan/proj/lvm/src/main.zig:52:17: 0x238ffe in main (lvm)
            repl() catch |err| {
                ^
/home/dan/Install/zig-linux-x86_64-0.11.0/lib/std/start.zig:574:37: 0x22824e in posixCallMainAndExit (lvm)
            const result = root.main() catch |err| {
                                    ^
/home/dan/Install/zig-linux-x86_64-0.11.0/lib/std/start.zig:243:5: 0x227d31 in _start (lvm)
    asm volatile (switch (native_arch) {
#

I'm literally just calling that function for a StringHashMap(void) that I have. Maybe the bug is related to the fact that the value type is void .

gritty crow
#

it can't be a bug in that function, which doesn't have the ability to mutate and corrupt the map

surreal thicket
#

@gritty crow ok, I narrowed this down. it only happens when I initialize a second hashset even if I don't use it (and when I call your get_map_keys function):

/// Return a new string hash set
pub fn newHashSet(gpa: Allocator) Allocator.Error!*std.StringHashMap(void) {
    var free = std.StringHashMap(void).init(gpa);
    // IF I UNCOMMENT THE TWO LINES BELOW I GET THE OVERFLOW
    // var other = std.StringHashMap(void).init(gpa);
    // _ = other;
    return &free;
}

pub fn main() !void {
    const string_hash_set = try newHashSet(gpa);
    const var_keys = try get_map_keys(gpa, vars.*); // this call is needed for the overflow to happen
    _ = var_keys;
    /// ...
}
gritty crow
#

the issue is return &free; which, being a pointer to a local, becomes invalid and after the function returns

#

so changing other code is going to make the program behave randomly and erratically