Inferring key/value types of a map

Hi! I am trying to figure out how to get the types of a key and a value in a map (knowing the type of a value of std.ArrayList would be nice as well :slight_smile:). The type information is needed to decode a slice of bytes into a given struct that could have arbitrary map or ArrayList types.

I thought I can do something like this, but I am getting error: struct 'hash_map.HashMapUnmanaged([]const u8,[]const u8,hash_map.StringContext,80).KV' has no member named 'key'. I would greatly appreciate any suggestions, thank you.

const std = @import("std");

pub fn main() void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();

    var m = std.hash_map.StringHashMap([]const u8).init(allocator);
    defer m.deinit();

    decode(@TypeOf(m));
}

fn decode(comptime T: type) void {
    switch (@typeInfo(T)) {
        .Struct => {
            // What are the types of K and V in the map?
            if (@hasDecl(T, "KV")) {
                std.debug.print("{s}: key type {any}, value type = {any}", .{
                    @typeName(T),
                    T.KV.key,
                    T.KV.value,
                });
            }
        },
        else => unreachable,
    }
}

This is because key is a field of KV, not a declaration, so you canโ€™t just access it through the type, you need to inspect the fields via @typeInfo(T.KV). Hereโ€™s an example using std.meta (which is a good reference for how to do reflection):

const std = @import("std");

pub fn main() void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    const allocator = gpa.allocator();

    var m = std.hash_map.StringHashMap([]const u8).init(allocator);
    defer m.deinit();

    decode(@TypeOf(m));
}

fn decode(comptime T: type) void {
    switch (@typeInfo(T)) {
        .Struct => {
            // What are the types of K and V in the map?
            if (@hasDecl(T, "KV")) {
                const Key = std.meta.FieldType(T.KV, .key);
                const Value = std.meta.FieldType(T.KV, .value);

                std.debug.print(
                    "{s}: key type {s}, value type = {s}",
                    .{ @typeName(T), @typeName(Key), @typeName(Value) },
                );
            }
        },
        else => unreachable,
    }
}
1 Like

Ah, that makes sense. Thank you very much!