Zig Tutorial - 7일차
포스트
취소

Zig Tutorial - 7일차

Zig 언어 공부를 시작합니다. 설치부터 문법 및 간단한 응용까지 다룹니다.

0. 튜토리얼 참고문서

1. Slices

  • 슬라이스 표현은 m..n 으로 한다. (m 에서 n 까지)
  • m 은 생략할 수 없다. (n 생략시 end 까지 포함)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const std = @import("std");
const testing = @import("std").testing;

pub fn main() !void {}

// value 합산하기
fn total(values: []const u8) usize {
    var sum: usize = 0;
    for (values) |v| sum += v;
    return sum;
}

test "slices" {
    const array = [_]u8{ 1, 2, 3, 4, 5 };  // 타입: [5]u8
    const slice = array[0..3];  // 타입: *const [3]u8

    // 배열을 출력하려면 format 으로 any 를 사용한다
    std.debug.print("slices: array = {any}\n", .{ array[0..] });
    std.debug.print("slices: slice = {any} ({any})\n", .{ slice, @TypeOf(slice) });
    try testing.expect(total(slice) == 6);
}

// 출력
// slices: array = { 1, 2, 3, 4, 5 }
// slices: slice = { 1, 2, 3 } (*const [3]u8)

참고: 포인터 타입들의 비교

  • *T : 역참조 가능 (ptr.*)
  • *[N]T : 배열의 포인터, 역참조 불가, 인덱싱 가능, 슬라이싱 가능
  • []T : 배열, 역참조 불가, 인덱싱 가능, 슬라이싱 가능
    • N 이 지정되지 않아 크기 모름 (런타임)
  • [*]T : 포인터의 배열, 역참조 불가, 인덱싱 가능, 슬라이싱 가능
    • ptr+1, ptr-1 같은 주소에 대한 연산 가능

2. Enums

  • 타입을 정하고, 태그에 값을 지정할 수 있다.
  • 내부에 struct 처럼 멤버 함수를 작성할 수 있다.
1
2
3
4
5
6
7
const AlphabetValue = enum(u4) { zero = 0, one = 11, two };

test "enum ordinal value" {
    try testing.expect(@intFromEnum(AlphabetValue.zero) == 1);
    try testing.expect(@intFromEnum(AlphabetValue.one) == 11);
    try testing.expect(@intFromEnum(AlphabetValue.two) == 12);
}

3. 구조체 Structs

  • 멤버 필드를 가진다. .{} 로 전달할 수도 있다.
    • var thing: Stuff = .{ .x = 10, .y = 20 };
  • enum 과 마찬가지로 함수와 변수도 가질 수 있다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
const Vec3 = struct { x: f32 = 0, y: f32 = 0, z: f32 = 0 };

test "struct usage" {
    const my_vector = Vec3{
        .x = 50,
        .y = 100,
        // .z = 50,
    };
    std.debug.print("struct: my_vector = {}\n", .{my_vector});
    // _ = my_vector;
}

// 출력
// struct: my_vector = Vec3{ .x = 5e1, .y = 1e2, .z = 0e0 }

멤버 함수

구조체는 구조체에 대한 포인터가 주어지면 필드에 액세스할 때 한 단계의 역참조가 자동으로 수행된다.

  • swap 함수에서 포인터인 self 에 대해 역참조 없이 .x 와 .y 를 사용한다.
    • 구조체가 아닌, 기본 타입인 경우에는 ptr.* 으로 역참조해서 연산했다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
const Stuff = struct {
    x: i32,
    y: i32,

    // 인수가 없으면, 자기 자신을 인자로 받는다
    fn swap(self: *Stuff) void {
        const tmp = self.x;
        self.x = self.y;
        self.y = tmp;
    }
};

test "automatic dereference" {
    var thing = Stuff{ .x = 10, .y = 20 };

    thing.swap();  // 자체 멤버 함수 호출
    try testing.expect(thing.x == 20);
    try testing.expect(thing.y == 10);
    std.debug.print("struct: thing = {}\n", .{thing});

    Stuff.swap(&thing);  // 참조 연산자를 붙인다 (포인터 전달)
    std.debug.print("struct: swaped = {}\n", .{thing});
}

// 출력
// struct: thing = Stuff{ .x = 20, .y = 10 }
// struct: swaped = Stuff{ .x = 10, .y = 20 }

4. 유니온 Unions

struct 와 유사하지만, 멤버 필드 중 하나만 사용 가능하다. (활성화)

1
2
3
4
5
6
7
8
9
10
const Result = union {
    int: i64,
    float: f64,
    bool: bool,
};

test "simple union" {
    var result = Result{ .int = 1234 };
    result.float = 12.34;  // <== 비활성화된 필드 접근 에러!!
}

union 을 enum 과 연결하여 어떤 멤버와 연결되어 있는 제어할 수 있다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
const Tag = enum { a, b, c };

const Tagged = union(Tag) { a: u8, b: f32, c: bool };

test "switch on tagged union" {
    var value = Tagged{ .b = 1.5 };

    // value 의 포인터를 페이로드 캡쳐(||) 하여 값에 접근할 수 있다
    switch (value) {
        .a => |*byte| byte.* += 1,
        .b => |*float| float.* *= 2,
        .c => |*b| b.* = !b.*,
    }
    try expect(value.b == 3);
}

참고: Payload Captures |value| 무언가의 값을 캡쳐하기

9. Review

  • 조금씩 복잡해진다. 왜 저렇게 만들었는지 의문이지만 나보다 머리 좋은 사람들이 만든 것이니 이유가 있겠지.

 
 

끝!   읽어주셔서 감사합니다.

이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.