泛型函数 #

一、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