Menu

Zero shape: 구조체 같은 레코드 정의하기

shape는 Zero의 구조체 같은 곱타입입니다. 선언하고, 값을 만들고, 필드를 읽고, 함수에 전달하는 방법을 공식 예제와 함께 정리했습니다.

이 페이지에는 실행 가능한 에디터가 있습니다 — 편집하고 실행하면 결과를 바로 볼 수 있습니다.

shape 선언하기

shape는 이름과 타입을 가진 필드를 모은 레코드 타입입니다.

shape Point {
    x: i32,
    y: i32,
}

각 부분:

  • shape가 선언을 시작합니다.
  • Point는 타입의 이름.
  • 중괄호 안에는 이름: 타입 형식의 필드 목록이 들어갑니다.

이 선언은 현재 스코프에 Point라는 새 타입을 추가합니다. i32 같은 내장 타입을 쓸 수 있는 곳이라면 어디서든 Point도 사용할 수 있게 됩니다.

shape 값 만들기

구조체 리터럴 표현식으로 인스턴스를 만듭니다.

let point = Point { x: 40, y: 2 }

리터럴은 shape 이름을 적고 각 필드를 할당합니다. 모든 필드는 반드시 있어야 해요. Zero는 빠진 필드를 0이나 null로 조용히 기본값 처리하지 않습니다. 빠뜨리면 컴파일러가 알려줍니다.

{
    "code": "FLD002",
    "message": "missing field: y",
    "line": 4
}

(에러 코드는 다를 수 있고, "암묵적 기본값 없음"이라는 원칙이 변하지 않는 부분입니다.)

타입을 명시적으로 어노테이션해서 드러내고 싶다면 그렇게 적어도 됩니다.

let point: Point = Point { x: 40, y: 2 }

필드 읽기

필드 접근은 점 문법을 씁니다.

let point = Point { x: 40, y: 2 }
let xVal  = point.x
let yVal  = point.y

point.xx 필드를 읽습니다. get_x() 메서드 같은 건 없어요. 필드는 평범한 데이터입니다.

완전한 작동 예제

언어 저장소에 있는 정석 point.0 예제입니다 — Run을 눌러 실행해 보세요.

위에서부터 따라가 봅시다.

  1. i32 필드 두 개를 가진 Point shape를 선언합니다.
  2. Point를 받아 그 필드의 합을 반환하는 sum 함수를 정의합니다.
  3. main에서 Point를 만들고 sum을 호출한 뒤 결과를 비교합니다.

세 단계, shape 관련 세 가지 개념(선언, 생성, 접근), 그리고 마지막의 한 가지 효과 — check world.out.write(...). sumWorld를 건드리지 않는다는 점에 주목하세요. 데이터에 대한 순수 함수이고, 시그니처가 그것을 분명히 드러냅니다.

중첩된 필드를 가진 shape

shape는 다른 shape를 포함해 어떤 타입의 값이든 가질 수 있습니다.

shape Range {
    start: i32,
    end: i32,
}

shape Segment {
    label: String,
    range: Range,
}

let seg = Segment {
    label: "warmup",
    range: Range { start: 0, end: 10 },
}

필드 접근도 예상대로 체인됩니다.

let len = seg.range.end - seg.range.start

제네릭 shape

shape의 필드가 타입 다형성을 가져야 할 때는 꺾쇠괄호 안에 타입 매개변수를 선언합니다.

shape Pair<T, U> {
    left: T,
    right: U,
}

인스턴스에서 그 매개변수를 구체 타입으로 고정합니다.

let intBytePair: Pair<i32, u8> = Pair { left: 40, right: 2_u8 }
let words: Pair<String, String> = Pair { left: "hello", right: "world" }

타입 별칭으로 자주 쓰는 매개변수화를 줄일 수 있습니다.

type BytePair = Pair<u8, u8>

let bytes: BytePair = Pair { left: 1_u8, right: 2_u8 }

제네릭 문서에서 shape뿐 아니라 함수까지 포함해 타입 매개변수를 더 깊이 다룹니다.

shape가 아닌

다른 언어의 "구조체"에서 기대할 법한 것들 중 shape가 의도적으로 포함하지 않은 것들입니다.

  • 메서드 없음. shape 선언은 그냥 데이터입니다. 동작은 shape를 매개변수로 받는 자유 함수에 있습니다. 함수에서 본 데이터와 효과의 분리와 같은 맥락입니다.
  • 상속 없음. shape는 다른 shape를 확장하지 않습니다. 공통 구조가 필요하다면 공통 필드로 빼거나 choice로 합타입을 만드세요.
  • 암묵적 생성자나 소멸자 없음. 생성은 구조체 리터럴 표현식입니다. 정리는 명시적입니다. 표준 라이브러리가 정리해야 할 자원을 노출하는 경우에는 숨은 RAII가 아니라 능력 스타일 API를 통해 처리합니다.
  • 비공개 필드 없음. shape의 모든 필드는 그 shape의 타입을 볼 수 있는 코드가 접근할 수 있습니다. 가시성은 타입 단위지 필드 단위가 아니에요.

패턴은 이렇습니다. shape는 단순하고 예측 가능한 레코드 타입이며, 나머지 모든 것은 그 위에 쌓아 올립니다.

shape와 choice, 언제 쓸까

간단 가이드:

  • shape는 값이 모든 필드를 함께 가질 때. Point는 늘 xy를 모두 갖습니다.
  • **choice**는 값이 여러 대안 _중 하나_일 때. Resultok 또는 err입니다.
  • **enum**은 대안이 추가 데이터를 운반하지 않을 때. 그냥 라벨이죠. 요일, 단순한 상태 같은 것들요.

이 세 가지 빌딩 블록 — shape(and), choice(or), enum(payload 없는 or) — 이 거의 모든 데이터 모델링 요구를 커버합니다.

다음 글: 제네릭

Pair<T, U>가 지나가듯 등장했죠. 다음 문서 제네릭에서는 shape와 함수 모두에서 타입 매개변수가 어떻게 동작하는지 설명하고, Zero 표준 라이브러리 전반에 나타나는 패턴까지 다룹니다.

자주 묻는 질문

Zero에서 shape가 뭔가요?

shape는 Zero의 구조체 같은 곱타입입니다. 이름이 붙고 타입이 있는 필드들을 가진 레코드죠. shape Name { field1: T1, field2: T2 }로 선언하고, Name { field1: v1, field2: v2 }로 값을 만들고, value.field1처럼 점 문법으로 필드를 읽습니다. 구조화된 데이터를 모델링할 때의 기본 블록입니다.

shape 값은 어떻게 만들죠?

shape 이름을 적고 각 필드를 할당하는 구조체 리터럴 표현식을 씁니다: let point = Point { x: 40, y: 2 }. 모든 필드는 반드시 채워져야 합니다. Zero는 빠진 필드를 조용히 기본값으로 채우지 않아요. 리터럴 안의 필드 순서가 선언 순서와 일치할 필요는 없습니다.

shape와 클래스는 어떻게 다른가요?

shape는 평범한 데이터입니다. 필드는 있지만 메서드도, 상속도, 암묵적 생성자도 없습니다. shape에 동작하는 함수는 그것을 명시적인 매개변수로 받습니다. 이런 분리가 언어를 작게 유지하고, shape를 만들거나 복사하는 비용을 예측 가능하게 만들어요. 숨은 vtable이나 소멸자가 없거든요.

Zero에서 shape도 제네릭이 될 수 있나요?

네. 꺾쇠괄호 안에 타입 매개변수를 선언합니다: shape Pair<T, U> { left: T, right: U }. 인스턴스에서 그 매개변수를 구체화하죠: Pair<i32, u8> 식으로요. 제네릭 shape는 표준 라이브러리 전반에 등장합니다. Maybe<T>, Span<T> 같은 것들이 모두 같은 아이디어 위에 만들어진 제네릭 shape나 합타입이에요.

shape를 함수에 전달하면 복사되나요, 참조되나요?

shape에 대한 기본 멘탈 모델은 값 전달입니다. 호출되는 쪽은 자기 논리적 사본을 보지, 호출자의 바인딩에 대한 참조를 보지 않아요. 1.0 이전 Zero의 정확한 메모리 모델은 아직 발전 중이며(표준 라이브러리 예제에서 명시적 참조 타입인 refmutref가 보일 거예요), 대부분의 애플리케이션 코드에서는 shape 매개변수를 값 타입 입력으로 다루면 됩니다.

Coddy programming languages illustration

Coddy로 코딩 배우기

시작하기