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가 지원하는 범위를 넘어서는 보다 폭넓은 메타프로그래밍 기능을 제공합니다.

0 조회
Back to Blog

관련 글

더 보기 »

Zig에서 배운 교훈

Zig 프로그래밍 언어는 의도적으로 작은 표준 라이브러리를 유지합니다. 엄격한 포함 기준을 충족하지 못하는 구성 요소는 제거되고 재배치됩니다.

제네릭이란 무엇인가?

Generics는 Java 5에서 도입된 기능으로, 다양한 데이터 타입과 함께 작동하는 classes, interfaces 및 methods를 만들 수 있게 합니다. 이들은…

Go 템플릿

Go 템플릿이란 무엇인가요? Go 템플릿은 Go에서 데이터를 일반 텍스트나 HTML 파일과 혼합하여 동적 콘텐츠를 생성하는 방법입니다. 이들은 자리표시자를 교체할 수 있게 해줍니다...

Go의 비밀스러운 삶: Panic과 Recover

Mastering Panic, Recover, and the “Must” Pattern in Go Chapter 21: The Emergency Brake > “내가 뭘 망친 것 같은데,” 이든이 속삭였다. 그는 자신의 터미널을 바라보고 있었다.