Zig vs Go: 제네릭

발행: (2026년 2월 10일 오전 08:07 GMT+9)
5 분 소요
원문: Dev.to

Source: Dev.to

번역할 텍스트가 제공되지 않았습니다. 번역을 원하는 본문을 알려주시면 한국어로 번역해 드리겠습니다.

Introduction

Go는 1.18 버전에서 제네릭을 도입했으며, 이를 통해 함수와 구조체를 타입으로 매개변수화할 수 있습니다. Zig는 오래전부터 comptime 키워드를 통해 컴파일 타임 제네릭을 지원해 왔으며, 이는 런타임 오버헤드가 없습니다.

함수 제네릭

Go

func doubleNumber[T constraints.Integer | constraints.Float](a T) T {
    return a * T(2)
}

doubledFloat := doubleNumber[float32](5.3)
fmt.Println(doubledFloat)

// type parameter inferred
doubledInteger := doubleNumber(5)
fmt.Println(doubledInteger)

Zig

fn doubleNumber(comptime T: type, a: T) T {
    return a * 2;
}

const doubledFloat = doubleNumber(f32, 5.3);
std.debug.print("{}\n", .{doubledFloat});

const doubledInteger = doubleNumber(i32, 5);
std.debug.print("{}\n", .{doubledInteger});

Go에서는 제네릭이 인터페이스에 의해 제한되며, 컴파일러가 컴파일 시점에 이를 검사합니다. Zig는 제공된 타입에 대해 함수를 인스턴스화할 때 컴파일‑타임 단계에서 오류를 평가합니다.

구조체 제네릭

Go

type Stack[T any, Q any] struct {
    Items      []T
    AlterItems []Q
    Index      int
}

intStack := Stack[int8, float32]{
    Items:      []int8{1, 2, 3, 4, 5},
    AlterItems: []float32{1.1, 2.1, 3.1, 4.7, 5.3},
    Index:      4,
}
fmt.Println(intStack.Items[intStack.Index], intStack.AlterItems[intStack.Index])

strStack := Stack[string, bool]{
    Items:      []string{"hello", "gophers"},
    AlterItems: []bool{false, true},
    Index:      0,
}
fmt.Println(strStack.Items[strStack.Index], strStack.AlterItems[strStack.Index])

Zig

fn Stack(comptime T: type, comptime Q: type) type {
    return struct {
        items: []const T,
        alterItems: []const Q,
        index: usize,
    };
}

const intArr = [_]i32{ 1, 2, 3, 4, 5 };
const floatArr = [_]f32{ 1.1, 2.1, 3.1, 4.7, 5.3 };
const intStack = Stack(i32, f32){
    .items = intArr[0..],
    .alterItems = floatArr[0..],
    .index = 4,
};
std.debug.print("{d} {d}\n", .{
    intStack.items[intStack.index],
    intStack.alterItems[intStack.index],
});

const strArr = [_][]const u8{ "hello", "ziguanas" };
const boolArr = [_]bool{ false, true };
const strStack = Stack([]const u8, bool){
    .items = &strArr,
    .alterItems = &boolArr,
    .index = 1,
};
std.debug.print("{s} {}\n", .{
    strStack.items[strStack.index],
    strStack.alterItems[strStack.index],
});

제약 vs. 인터페이스

Go에서는 제약(constraints)을 인터페이스로 표현합니다. 제네릭 함수가 구조체를 매개변수로 받을 때, 해당 구조체는 함수 내부에서 사용되는 메서드를 구현하고 있어야 합니다.

type IdTrackable interface {
    GetID() string
}

type Article struct {
    ID       string
    Name     string
    Category string
}
func (a Article) GetID() string { return a.ID }

type SKU struct {
    ID        string
    ArticleID string
    Available bool
}
func (s SKU) GetID() string { return s.ID }

func findById[T IdTrackable](items []T, id string) (*T, error) {
    for _, item := range items {
        if item.GetID() == id {
            return &item, nil
        }
    }
    return nil, errors.New("not found")
}

Example: Find by ID

Go 사용법

articles := []Article{
    {ID: "a1", Name: "Laptop", Category: "Electronics"},
    {ID: "a2", Name: "Book", Category: "Education"},
}

article, err := findById(articles, "a1")
if err == nil {
    fmt.Printf("Found article: %+v\n", *article)
}

Zig 구현

const Article = struct {
    id: []const u8,
    name: []const u8,
    category: []const u8,
};

const SKU = struct {
    id: []const u8,
    articleId: []const u8,
    available: bool,
};

fn findById(comptime T: type, items: []const T, idx: []const u8) !T {
    for (items) |item| {
        if (std.mem.eql(u8, @field(item, "id"), idx)) {
            return item;
        }
    }
    return error.NotFound;
}
const articles = [_]Article{
    .{ .id = "a1", .name = "Laptop", .category = "Electronics" },
    .{ .id = "a2", .name = "Book", .category = "Education" },
};

const art = try findById(Article, &articles, "a2");
std.debug.print("Found article: {}\n", .{art});

Zig에서는 제네릭 함수가 각 고유 타입마다 컴파일 타임에 인스턴스화되어, 런타임 비용 없이 별도의 특수화된 버전을 생성합니다.

결론

Zig의 컴파일 타임 메타프로그래밍은 강력하고 오버헤드가 없는 제네릭 접근 방식을 제공하며, Go의 제네릭은 인터페이스에 의해 제한된 런타임 없이 타입 파라미터에 의존합니다. 두 언어 모두 타입 안전하고 재사용 가능한 코드를 가능하게 하지만, Zig는 현재 Go가 지원하는 범위를 넘어서는 보다 폭넓은 메타프로그래밍 기능을 제공합니다.

Back to Blog

관련 글

더 보기 »

Go의 비밀스러운 삶: ‘defer’ 문

챕터 20: The Stacked Deck Ethan의 데스크탑 PC 팬이 크게 돌고 있었다. 그는 오류 메시지를 끊임없이 뿜어내는 터미널을 마치 부서진 불꽃처럼 바라보고 있었다.

나는 C(예, C)로 게임을 만든다

왜 나는 C로 게임을 만드는가, 그래, C. 나는 특이한 존재다. 최근에 만들고 있는 모든 솔로‑프로젝트 게임은 바닐라 C로 작성했다. 아무도 이걸 안 하니까, 그래서 나는…