Since we have a variable number of possible return types, I like to use helper functions to deduce these - to see examples of this, check out this thread: Implementing Generic Concepts on Function Declarations - #13 by AndrewCodeDev
Here’s an example that works using a helper function to deduce the type.
const std = @import("std");
const Info = struct {
name: []const u8,
age: u8,
};
pub fn InstanceFieldType(comptime T: type, comptime field_name: [] const u8) type {
comptime var instance: T = undefined;
return @TypeOf(@field(instance, field_name));
}
const InfoStorage = struct {
const Self = @This();
storage: [30]Info,
pub fn readField(
self: *Self,
index: usize,
comptime field_name: []const u8,
) InstanceFieldType(Info, field_name) {
const storage_ref = &self.storage[index];
return @field(storage_ref, field_name);
}
};
pub fn main() void {
var info: InfoStorage = undefined;
info.storage[0] = .{ .name = "Jane", .age = 42 };
std.debug.print("{d}\n", .{@field(&info.storage[0], "age")});
std.debug.print("{d}\n", .{info.readField(0, "age")});
}
I’m also changing the self parameter type to be a pointer-to-self. It clarifies the intent that this is not supposed to be a copy.
– edited for for one more example –
You could also do this, but I don’t recommend it for the general case.
pub fn readField(
self: *Self,
index: usize,
comptime field_name: []const u8,
) @TypeOf(@field(self.storage[0], field_name)) { // adding &self.storage is optional here.
const storage_ref = &self.storage[index];
return @field(storage_ref, field_name);
}
I don’t recommend it because accessing indices will at least require the array to have a size of 1. In your case, it’s kind of obvious it will work because you have 30 as the size. The reason I like the other method better is because it guarantees that there will be at least one instance available because we created it in the function itself.