पाठ 14 / 25
Generic Types and Functions
Write generic data structures with functions that return types.
Functions that return types
A generic function takes a comptime T: type parameter, or uses anytype to infer the parameter's type at each call: fn max(comptime T: type, a: T, b: T) T. A generic data structure is a function that returns a type: fn Stack(comptime T: type) type { return struct { ... }; }, used as Stack(u32). Inside, @This() names the returned struct, conventionally as const Self = @This();. The standard library uses this everywhere: std.ArrayList(T), std.AutoHashMap(K, V), std.PriorityQueue(T, Context, compareFn). Because generics are just compile-time function calls, you can add constraints and errors in plain code: check @typeInfo(T) and call @compileError with a helpful message if a type is unsupported. Instantiations with the same arguments are memoised, so Stack(u32) is always the same type. Zig has no interfaces or traits keyword; runtime polymorphism uses explicit vtables (a struct of function pointers plus a context pointer), as std.mem.Allocator and std.Io.Writer do, or tagged unions when the set of implementations is closed.
A generic bounded stack
A function returning a struct type, with comptime capacity.
const std = @import("std");
fn BoundedStack(comptime T: type, comptime capacity: usize) type {
return struct {
items: [capacity]T = undefined,
len: usize = 0,
const Self = @This();
pub fn push(self: *Self, value: T) error{Full}!void {
if (self.len == capacity) return error.Full;
self.items[self.len] = value;
self.len += 1;
}
pub fn pop(self: *Self) ?T {
if (self.len == 0) return null;
self.len -= 1;
return self.items[self.len];
}
pub fn peek(self: *const Self) ?T {
return if (self.len == 0) null else self.items[self.len - 1];
}
};
}
fn largest(comptime T: type, values: []const T) ?T {
if (values.len == 0) return null;
var best = values[0];
for (values[1..]) |v| best = @max(best, v);
return best;
}
test "bounded stack" {
var s: BoundedStack(u32, 2) = .{};
try s.push(10);
try s.push(20);
try std.testing.expectError(error.Full, s.push(30));
try std.testing.expectEqual(@as(?u32, 20), s.pop());
try std.testing.expectEqual(@as(?u32, 10), s.peek());
}
test "largest" {
try std.testing.expectEqual(@as(?i32, 9), largest(i32, &.{ 3, 9, -2 }));
try std.testing.expectEqual(@as(?f64, null), largest(f64, &.{}));
}A mould factory
BoundedStack is not a stack; it is a factory that makes stack moulds. Ask for a mould for u32 with room for 2, and the compiler casts it once; every later request for the same mould gets the same one.
त्वरित जाँच: How are generic data structures usually written in Zig?
- With a template keyword
- With macros
- With inheritance
- As functions that take comptime parameters and return a type
Answer
As functions that take comptime parameters and return a type — For example, fn Stack(comptime T: type) type { return struct { ... }; }.