泛型函数 #
一、comptime 参数 #
1.1 基本 comptime 参数 #
comptime 参数在编译时确定:
zig
const std = @import("std");
fn repeat(comptime times: usize, char: u8) void {
comptime var i: usize = 0;
inline while (i < times) : (i += 1) {
std.debug.print("{c}", .{char});
}
std.debug.print("\n", .{});
}
pub fn main() void {
repeat(5, '*');
repeat(10, '-');
}
1.2 类型参数 #
使用 type 作为参数实现泛型:
zig
const std = @import("std");
fn maxValue(comptime T: type, a: T, b: T) T {
return if (a > b) a else b;
}
pub fn main() void {
std.debug.print("Max i32: {}\n", .{maxValue(i32, 10, 20)});
std.debug.print("Max f64: {d}\n", .{maxValue(f64, 3.14, 2.71)});
}
1.3 泛型函数约束 #
zig
const std = @import("std");
fn printValue(comptime T: type, value: T) void {
const info = @typeInfo(T);
switch (info) {
.int => std.debug.print("Integer: {}\n", .{value}),
.float => std.debug.print("Float: {d}\n", .{value}),
.bool => std.debug.print("Boolean: {}\n", .{value}),
.pointer => |ptr| {
if (ptr.size == .slice and ptr.child == u8) {
std.debug.print("String: {s}\n", .{value});
}
},
else => std.debug.print("Unknown type\n", .{}),
}
}
pub fn main() void {
printValue(i32, 42);
printValue(f64, 3.14);
printValue(bool, true);
printValue([]const u8, "hello");
}
二、泛型数据结构 #
2.1 泛型结构体 #
zig
const std = @import("std");
fn ArrayList(comptime T: type) type {
return struct {
items: []T,
len: usize,
capacity: usize,
allocator: std.mem.Allocator,
const Self = @This();
fn init(allocator: std.mem.Allocator) Self {
return .{
.items = &.{},
.len = 0,
.capacity = 0,
.allocator = allocator,
};
}
fn deinit(self: *Self) void {
if (self.capacity > 0) {
self.allocator.free(self.items.ptr[0..self.capacity]);
}
}
fn append(self: *Self, item: T) !void {
if (self.len >= self.capacity) {
const new_capacity = if (self.capacity == 0) 4 else self.capacity * 2;
const new_items = try self.allocator.alloc(T, new_capacity);
@memcpy(new_items[0..self.len], self.items);
if (self.capacity > 0) {
self.allocator.free(self.items.ptr[0..self.capacity]);
}
self.items = new_items;
self.capacity = new_capacity;
}
self.items[self.len] = item;
self.len += 1;
}
fn get(self: Self, index: usize) ?T {
if (index >= self.len) return null;
return self.items[index];
}
};
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var list = ArrayList(i32).init(allocator);
defer list.deinit();
try list.append(1);
try list.append(2);
try list.append(3);
for (0..list.len) |i| {
std.debug.print("list[{}] = {}\n", .{ i, list.get(i).? });
}
}
2.2 泛型函数返回泛型类型 #
zig
const std = @import("std");
fn Pair(comptime T: type) type {
return struct {
first: T,
second: T,
};
}
fn makePair(comptime T: type, a: T, b: T) Pair(T) {
return .{ .first = a, .second = b };
}
pub fn main() void {
const int_pair = makePair(i32, 10, 20);
const float_pair = makePair(f64, 3.14, 2.71);
std.debug.print("Int pair: ({}, {})\n", .{ int_pair.first, int_pair.second });
std.debug.print("Float pair: ({d}, {d})\n", .{ float_pair.first, float_pair.second });
}
三、编译时多态 #
3.1 使用 anytype #
anytype 允许任意类型:
zig
const std = @import("std");
fn printLength(items: anytype) void {
std.debug.print("Length: {}\n", .{items.len});
}
pub fn main() void {
const arr = [_]i32{ 1, 2, 3, 4, 5 };
const slice: []const i32 = &arr;
printLength(arr);
printLength(slice);
printLength("hello");
}
3.2 类型约束 #
zig
const std = @import("std");
fn sum(items: anytype) @typeInfo(@TypeOf(items)).pointer.child {
const T = @typeInfo(@TypeOf(items)).pointer.child;
var total: T = 0;
for (items) |item| {
total += item;
}
return total;
}
pub fn main() void {
const arr = [_]i32{ 1, 2, 3, 4, 5 };
std.debug.print("Sum: {}\n", .{sum(&arr)});
}
3.3 鸭子类型 #
zig
const std = @import("std");
const Dog = struct {
name: []const u8,
fn speak(self: @This()) void {
std.debug.print("{s} says: Woof!\n", .{self.name});
}
};
const Cat = struct {
name: []const u8,
fn speak(self: @This()) void {
std.debug.print("{s} says: Meow!\n", .{self.name});
}
};
fn makeItSpeak(animal: anytype) void {
animal.speak();
}
pub fn main() void {
const dog = Dog{ .name = "Buddy" };
const cat = Cat{ .name = "Whiskers" };
makeItSpeak(dog);
makeItSpeak(cat);
}
四、泛型算法 #
4.1 泛型排序 #
zig
const std = @import("std");
fn lessThan(comptime T: type) fn (T, T) bool {
return struct {
fn compare(a: T, b: T) bool {
return a < b;
}
}.compare;
}
fn bubbleSort(comptime T: type, items: []T, compare: fn (T, T) bool) void {
var i: usize = items.len;
while (i > 1) : (i -= 1) {
for (items[0 .. i - 1], 0..) |*item, j| {
if (compare(items[j + 1], item.*)) {
const temp = item.*;
item.* = items[j + 1];
items[j + 1] = temp;
}
}
}
}
pub fn main() void {
var numbers = [_]i32{ 5, 2, 8, 1, 9, 3 };
bubbleSort(i32, &numbers, lessThan(i32));
std.debug.print("Sorted: {any}\n", .{numbers});
}
4.2 泛型搜索 #
zig
const std = @import("std");
fn binarySearch(comptime T: type, items: []const T, target: T) ?usize {
var left: usize = 0;
var right: usize = items.len;
while (left < right) {
const mid = left + (right - left) / 2;
if (items[mid] == target) {
return mid;
} else if (items[mid] < target) {
left = mid + 1;
} else {
right = mid;
}
}
return null;
}
pub fn main() void {
const numbers = [_]i32{ 1, 3, 5, 7, 9, 11, 13, 15 };
if (binarySearch(i32, &numbers, 7)) |index| {
std.debug.print("Found 7 at index {}\n", .{index});
} else {
std.debug.print("Not found\n", .{});
}
}
五、实战示例 #
5.1 泛型栈 #
zig
const std = @import("std");
fn Stack(comptime T: type) type {
return struct {
items: []T,
top: usize,
allocator: std.mem.Allocator,
const Self = @This();
fn init(allocator: std.mem.Allocator, capacity: usize) !Self {
return .{
.items = try allocator.alloc(T, capacity),
.top = 0,
.allocator = allocator,
};
}
fn deinit(self: *Self) void {
self.allocator.free(self.items);
}
fn push(self: *Self, item: T) !void {
if (self.top >= self.items.len) return error.StackFull;
self.items[self.top] = item;
self.top += 1;
}
fn pop(self: *Self) ?T {
if (self.top == 0) return null;
self.top -= 1;
return self.items[self.top];
}
fn peek(self: Self) ?T {
if (self.top == 0) return null;
return self.items[self.top - 1];
}
fn isEmpty(self: Self) bool {
return self.top == 0;
}
};
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var stack = try Stack(i32).init(allocator, 10);
defer stack.deinit();
try stack.push(1);
try stack.push(2);
try stack.push(3);
while (!stack.isEmpty()) {
std.debug.print("Popped: {}\n", .{stack.pop().?});
}
}
5.2 泛型映射 #
zig
const std = @import("std");
fn HashMap(comptime K: type, comptime V: type) type {
return struct {
const Entry = struct {
key: K,
value: V,
used: bool,
};
entries: []Entry,
allocator: std.mem.Allocator,
size: usize,
const Self = @This();
fn init(allocator: std.mem.Allocator, capacity: usize) !Self {
const entries = try allocator.alloc(Entry, capacity);
for (entries) |*entry| {
entry.used = false;
}
return .{
.entries = entries,
.allocator = allocator,
.size = 0,
};
}
fn deinit(self: *Self) void {
self.allocator.free(self.entries);
}
fn hash(key: K) usize {
return @intCast(@as(u64, @intCast(key)) % 31);
}
fn put(self: *Self, key: K, value: V) !void {
if (self.size * 2 >= self.entries.len) return error.HashMapFull;
var index = hash(key) % self.entries.len;
while (self.entries[index].used and self.entries[index].key != key) {
index = (index + 1) % self.entries.len;
}
if (!self.entries[index].used) {
self.size += 1;
}
self.entries[index] = .{
.key = key,
.value = value,
.used = true,
};
}
fn get(self: Self, key: K) ?V {
var index = hash(key) % self.entries.len;
const start_index = index;
while (self.entries[index].used) {
if (self.entries[index].key == key) {
return self.entries[index].value;
}
index = (index + 1) % self.entries.len;
if (index == start_index) break;
}
return null;
}
};
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
var map = try HashMap(i32, []const u8).init(allocator, 16);
defer map.deinit();
try map.put(1, "one");
try map.put(2, "two");
try map.put(3, "three");
if (map.get(2)) |value| {
std.debug.print("Key 2: {s}\n", .{value});
}
}
六、最佳实践 #
6.1 使用 comptime 优化 #
zig
// 好:编译时计算
fn factorial(comptime n: u32) u32 {
if (n <= 1) return 1;
return n * factorial(n - 1);
}
pub fn main() void {
const result = comptime factorial(10);
std.debug.print("10! = {}\n", .{result});
}
6.2 类型安全 #
zig
// 好:明确的类型约束
fn add(comptime T: type, a: T, b: T) T {
return a + b;
}
// 避免:过度使用 anytype
fn addAny(a: anytype, b: anytype) @TypeOf(a + b) {
return a + b;
}
七、总结 #
泛型函数要点:
| 特性 | 说明 |
|---|---|
| comptime | 编译时参数 |
| type | 类型参数 |
| anytype | 任意类型 |
| 泛型结构体 | 返回类型的函数 |
下一步,让我们学习指针!
最后更新:2026-03-27