@hasDecl — 네임스페이스에 선언이 있는지 확인하기

@hasDecl — 네임스페이스에 선언이 있는지 확인하기

어떤 타입 안에 특정 이름의 공개(public) 선언이 들어 있는지 궁금할 때가 있어요. 예컨대 "이 API에 blah라는 선언이 있나?" 같은 확인을 코드 안에서 직접 물어볼 수 있으면 좋겠죠. 바로 그 역할을 하는 내장 함수가 @hasDecl이에요. 네임스페이스(구조체·열거형·파일 등)와 이름을 받아서, 그 이름의 공개 선언이 존재하면 true, 없으면 false를 돌려줍니다.

출처: Zig Documentation

본문

시그니처는 이렇게 생겼어요. 네임스페이스와 이름 둘 다 컴파일 타임 값이죠.

@hasDecl(comptime Namespace: type, comptime name: []const u8) bool

직역하자면 "Namespacename과 일치하는 공개 선언이 있는지 여부를 돌려준다"예요. 실제 동작을 테스트로 확인해 볼게요.

const std = @import("std");
const expect = std.testing.expect;

const Foo = struct {
    nope: i32,

    pub var blah = "xxx";
    const hi = 1;
};

test "@hasDecl" {
    try expect(@hasDecl(Foo, "blah"));

    // @hasDecl returns false for private declarations.
    try expect(!@hasDecl(Foo, "hi"));

    // @hasDecl is for declarations; not fields.
    try expect(!@hasDecl(Foo, "nope"));
    try expect(!@hasDecl(Foo, "nope1234"));
}
$ zig test test_hasDecl_builtin.zig
1/1 [email protected]
All 1 tests passed.

주석이 핵심을 다 설명해 주고 있는데요, 한 가지만 더 짚어볼게요. Foo 안에 hi라는 선언이 존재하긴 하지만 const hipub이 아니에요. 그래서 @hasDecl(Foo, "hi")false를 반환하죠. @hasDecl공개 선언만 대상으로 한다는 게 여기서 드러나요. 그리고 nope는 필드(field)지 선언(declaration)이 아니기 때문에, 테스트에서 보듯 "nope""nope1234"도 모두 false예요.

주의사항

@hasDecl을 써서 "이 API에 어떤 선언이 있는지/없는지를 기준으로 조건부 컴파일"을 하면, 데드 코드(dead code) 가 몰래 생길 위험이 있어요. 예를 들어 필드 이름이 나중에 바뀌면, @hasDecl을 쓰던 코드는 원래 의도한 블록을 켜거나 끄는 데 실패하면서도 컴파일은 그대로 성공해 버려요. 심지어 @hasDecl에 넘기는 이름에 단순한 오타가 있어도 컴파일러는 눈치채지 못합니다.

그래서 가능하면 특정 선언의 존재 여부를 확인하는 다른 방식을 쓰는 게 더 안전해요. 예를 들어 대상 운영체제(operating system)에 따라 특정 함수가 있는지 확인하고 싶을 땐, @hasDecl보다 이렇게 쓰는 편이 유지보수하기 쉬워요.

@import("builtin").target.os.tag

더 알아보기 (Learn more)

필드(선언이 아닌)를 확인할 땐 @hasField를 써요.