Zig 첫인상

maxtnuk·2022년 7월 16일
4

저는 지금까지 Rust로 쭉 개발을 해오다가 조금 충격적인 소식을 듣게 되었습니다.
javascript engine으로 bun이 기존 node, deno에 비해 굉장히 빠른 성능을 보장을 한다는 내용이 었습니다.
node는 그렇다 쳐도 Rust base인 deno보다 성능이 좋게 나왔다는 게 신기했습니다.
물론 지금은 베타여서 다른 문제점이 있을 수 있지만 왜 zig가 이런 게 가능한지 포스트로 적을려고 합니다.

Zig란

zig 공식 홈페이지
Andrew Kelley가 개발한 정적 타입의 언어로 llvm을 벡엔드로 사용하고 있습니다.
언어 또한 굉장히 단순하게 설계되어 있어서 500줄 밖에 되지 않습니다. 참고.
몇가지 흥미로운 특징을 뽑으면

  • 별도의 ffi와 바인딩 없이 c와 호환이 됩니다.
    • 이는 libc가 정적으로 내장이 되어서 가능한 것입니다.
    • 그래서 c 파일을 컴파일도 가능하고 이를 이용해 cmake와 같은 빌드툴로 사용할 수 있습니다.
  • 메모리 수동관리가 가능합니다.
    • 러스트에서는 Allocator 지정해서 구현할 수 있지만 low level로 하기 어려운 점이 있습니다.
    • 하지만 zig에서는 쉽게 커스텀 Allocator를 구현할 수 있습니다.
    • 심지어 힙 할당을 제외하고 프로그램을 짤 수 있습니다.
  • 표준 라이브러리를 완전 선택적으로 가져올 수 있어서 성능 최적화에 유리합니다.
  • 빌드 캐시 시스템이 있어서 빠르게 빌드가 가능합니다.

꽤 흥미로운 특징들입니다. 그래도 한번 사용을 해봐야 제대로 알 것 같습니다.
자세한 컴파일러 설명은 여기 밑에 참고하시면 좋습니다.
zig compile internal
zig document

Zig 사용 준비

먼저 zig 자체를 설치를 해야 합니다. zig 설치 방법 참고해서 진행하면 됩니다.
다음으로 개발을 위해 language server를 설치를 합니다.
zls github에 들어가서 파일을 받고 밑의 명령어를 실행해주세요 (linux 기준)

chmod +x zls
./zls config

그러면 설정이 되고 이제 vscode와 연동하면 됩니다.
vscode zls를 설치하고 zls path를 설정을 하면 모든 준비가 끝났습니다.

Zig 첫 프로그램

mkdir hello-world
cd hello-world
zig init-exe

러스트일 경우에는 cargo new hello-world --bin 하면 디렉토리 만들어지고 프로젝트가 생성이 되는데 이부분은 조금 아쉬운 것 같습니다.
다음과 같은 프로젝트 구조를 가지게 됩니다.

├── build.zig
├── src
│   └── main.zig
  • build.zig: 프로젝트 빌드를 하는데 필요한 파일
  • src/main.zig: main 함수를 포함한 파일

main.zig의 파일

const std = @import("std");

pub fn main() anyerror!void {
    std.log.info("All your codebase are belong to us.", .{});
}

test "basic test" {
    try std.testing.expectEqual(10, 3 + 7);
}

위의 코드 보면서 처음 느낀 점은

  • 모듈 import 방식을 js와 유사한 형태로 하고 있다.
  • go와 유사하게 error와 return을 동시에 정의하는 형태로 되어 있다.
  • test가 아예 키워드로 쓰이고 있다.
  • try catch 형태가 아닌 try 를 쓰고 에러 여부를 확인하고 있다.

이를 zig build로 빌드를 하면 다음과 같은 결과물이 나옵니다.

# tree output
├── build.zig
├── src
│   └── main.zig
├── zig-cache
│   ├── h
│   ├── o
│   └── z
└── zig-out
    └── bin
  • zig-cache: 빌드 캐시를 모아 놓은 디렉토리
  • zig-out: 바이너리 결과물이 나오는 디렉토리

zig build run을 치면 "All your codebase are belong to us." 이라는 출력이 나옵니다.
zig build test를 치면 프로젝트내의 모든 테스트들이 실행이 되고 결과를 알려줍니다.

Zig 문법

여기서 모든 zig 문볍을 알려주기는 어려워서 몇가지 인상적인 걸 얘기하겠습니다.

  • defer 를 통해 block에서 벗어날 때 값을 지정할 수 있습니다.
  • error와 타입을 합쳐서 표현하는 게 신박했습니다.
    • error!T 라고 하면 T타입 이되 error를 가질 수 있는 형태로 표현할 수 있습니다.
  • try, catch가 분리되어 있습니다. 그래서 try 부분을 임의로 넓히지 않고 원하는 타이밍에 catch를 할 수 있습니다.
  • error 끼리 서로 합칠 수 있습니다.const C = A || B
  • 러스트의 unsafe 비슷하게 @setRuntimeSafety 로 해당 블록에서의 safety를 해제할 수 있습니다.
  • 숫자 0,null인 변수에 포인터를 만들 수 없습니다. (숫자 0은 왜?..)
    • const variable에 포인터를 하면 가리키는 곳의 값을 바꿀 수 없는 포인터가 됩니다.
      • 러스트에서는 & T, &mut T로 구분을 하는데 이 부분은 좀 아쉽습니다.
  • enum,struct 정의를 하는 부분에서 함수 정의도 할 수 있습니다.(가독성이 좀 떨어질 것 같습니다.)
    • global 변수도 가질 수 있습니다.
  • union을 지원합니다.
  • %을 이용한 wrapping operator를 지원합니다.
    • 이 오퍼레이터는 오버플로우를 허용하는 연산이 됩니다.
    • 예를 들어 var a:u8 = 255; a +%= 1; 라고 했을 때 a는 0이 됩니다.
  • 러스트의 라이프 타임과 유사하게 block자체에 label을 달아서 원하는 위치에 break를 할 수 있습니다.
  • while, forelse 구문을 넣을 수 있습니다.
    • 이때 의미는 break를 하지 않고 반복이 끝났을 때 호출이 됩니다.
  • if, while에서 값을 capturing을 할 수 있습니다.
    if (b) |*value| [ ..
    이렇게 할시 b가 null이 아닐 경우,b 안의 값이 value에 할당할 수 있습니다.
    • 이를 Payload Capture라고 부릅니다.
  • inline 과 유사하게 Comptime 키워드를 쓰면 컴파일 타임에 연산하겠끔 강제할 수 있습니다.
  • Anonymous Struct를 통해서 임의 struct를 만들 수 있습니다.
    • js의 object와 같은 느낌입니다.

여기까지 제가 느꼈던 zig에 대한 생각입니다.
러스트는 확실히 정의를 하고 진행을 해서 이해는 되지만 zig는 새로운 키워드랑 전에 보지 못한 syntax를 보아서 익숙하지는 않네요.
좀 더 더 써보고 나중에 심화로 알려드리겠습니다.

profile
Rust로 무난하게 개발하는 사람

1개의 댓글

comment-user-thumbnail
2023년 6월 15일

잘봤습니다!

답글 달기