rokdocs
v0.1

2.1. 타입 표현식

타입 표현식은 소스 코드에서 타입을 가리키는 표기입니다. 변수 선언의 : 뒤, 함수 매개변수의 : 뒤, 반환 타입의 -> 뒤, 제네릭 인자, 형 변환의 as 뒤 등 여러 위치에 등장합니다.

본 문서는 형식만 정의하며, 어떤 이름이 어떤 타입을 가리키는지는 표준 라이브러리가 결정합니다.

2.1.1. 기본 형태

타입 표현식의 기본 문법은 다음과 같이 정의됩니다.

type_expr   ::= sum_type
sum_type    ::= unary_type ("|" unary_type)*
unary_type  ::= base_type "?"*                  // T? - optional (2.1.5a), `??` 는 `?` 두 개
base_type   ::= path_type
              | fn_type
              | tuple_type
              | array_type
              | any_type
              | "(" type_expr ")"               // 그룹
any_type    ::= "any" ( trait_ref | "(" trait_ref ("+" trait_ref)* ")" )?
trait_ref   ::= identifier ("::" identifier)* trait_args?
trait_args  ::= "<" trait_arg ("," trait_arg)* ","? ">"
trait_arg   ::= type_expr | identifier "=" type_expr
path_type   ::= identifier ("::" identifier)* type_args?
type_args   ::= "<" type_expr ("," type_expr)* ","? ">"
fn_type     ::= "unsafe"? ("extern" string_lit)? "fn" "( (type_expr ("," type_expr)*)? ")" ("throws" ("(" type_expr ")")?)? ("->" unary_type)? fn_bound*
fn_bound    ::= "+" ("Send" | "Sync")
tuple_type  ::= "(" ")"
              | "(" type_expr "," ")"
              | "(" type_expr ("," type_expr)+ ","? ")"
array_type  ::= "[" type_expr ";" const_expr "]"

extern "C" fn(A, B) -> R 은 C 함수 포인터 타입입니다 (5.9.4). ABI 문자열은 "C" 만 쓸 수 있으며, 그 밖은 에러입니다.

이를 형태별로 정리하면 다음 표와 같습니다.

형태예
단순 이름i32, bool, Point
경로rok::std::time::Duration
제네릭 적용List<i32>, Dict<str, f64>, Pair<A, B>
합 타입 (|)i32 | f32, T | none
optional (?)i32?, (i32 | str)?, List<T>?
함수 타입fn(i32, i32) -> i32, fn() -> T + Send
튜플 타입(i32, str), (i32,)
배열 타입[T; N] (고정 크기)
동적 슬롯any, any Error, any (Display + Hash)

각 형태의 정확한 의미는 아래에서 차례로 정의합니다.

T::Assoc 형태의 associated type projection (I::Item, F::Output) 도 타입입니다. 이는 경로 형태 (path_type) 의 하나이며, T 가 그 associated type 을 가진 트레잇으로 제약된 타입 매개변수일 때 유효합니다 (타입 경로).

fn zip<I: Iterator, J: Iterator>(a: I, b: J) -> List<(I::Item, J::Item)> { ... }

2.1.2. 단순 이름

단순 이름은 식별자 한 개로 구성되며, 식별자 형식을 따릅니다.

i32
bool
Point
변수타입

이 이름이 어떤 항목을 가리키는지는 이름 해소(name resolution) 단계에서 결정됩니다. 현재 모듈에 정의된 항목이나 use 로 가져온 항목 등이 후보가 됩니다.

2.1.3. 경로

여러 식별자가 :: 로 이어진 형태도 타입 표현식으로 사용할 수 있습니다.

rok::std::time::Duration
rok::core::collections::List
pkg::geom::Point

여기에서 :: 토큰은 구두점과 연산자 토큰 에 정의된 경로 구분자입니다. 경로의 마지막 토큰이 타입 이름이 되고, 앞부분은 그 타입을 어디서 찾을지를 가리키는 모듈 또는 항목 경로의 역할을 합니다.

경로의 의미와 해석 규칙은 타입 경로 에서 정의하므로, 본 문서에서는 타입 표현식의 한 형태로 경로가 등장할 수 있다는 사실만 명시합니다.

2.1.4. 제네릭 적용

타입 이름 뒤에 <...> 를 붙여 타입 인자를 명시할 수 있습니다.

List<i32>
Dict<str, f64>
Pair<A, B>
Result<T, E>
Array<f64, 3>          // const 제네릭 (값 인자)

이때 따라야 할 규칙이 있습니다. 먼저 인자 사이는 콤마로 구분합니다. 그리고 인자에는 다른 타입 표현식이 올 수 있으며, const 제네릭의 경우에는 const 표현식도 허용됩니다. 한편 빈 <> 는 허용되지 않으므로, 인자가 없을 때는 <> 자체를 적지 않습니다. 또한 후행 콤마(<A, B,>) 는 허용됩니다.

값 인자(예: Array<f64, 3>) 의 정확한 의미는 const 제네릭 에서 정의합니다.

표현식 위치에서의 turbofish

표현식 안에서 제네릭 인자를 명시할 때는 ::<...> 형태를 사용하며, 이는 표현식 사양에서 정의합니다. 본 문서에서는 타입 표현식 위치의 <...> 만 다룹니다.

2.1.5. 합 타입 (|)

여러 타입 중 하나의 값을 가지는 합 타입은 | 로 표기합니다.

i32 | f32
T | none
A | B | C | D

| 로 구분된 각 항은 서로 다른 타입 표현식이며, 결과 합 타입은 그 항들 중 하나의 값을 갖습니다. 의미와 메모리 표현은 합 타입 에서 정의합니다.

한편 | 토큰의 우선순위는 다른 타입 결합자보다 낮습니다. 따라서 i32 | List<f32> 는 i32 | (List<f32>) 로 묶이는데, 이는 제네릭 적용이 합 타입보다 강하게 묶이기 때문입니다.

2.1.5a. optional 타입 (T?)

후위 ? 는 optional 타입을 만듭니다. T? 는 Maybe<T> (type Maybe<T> = T | none;) 와 똑같이 동작합니다. 곧 ? 의 피연산자는 제네릭 항이며, 합 타입의 층 규칙 이 적용됩니다.

i32?                // i32 | none
(i32 | str)?        // i32 | str | none - 겹침 없음, 펼침
(i32 | none)?       // 두 층 - 바깥 none ≠ 안쪽 none
i32??               // 두 층 - (i32 | none)? 과 같음
i32 | none          // 적은 합 - 늘 평평한 i32 | none
  • 후위 ? 는 | 보다, 그리고 함수 타입의 -> 보다 강하게 묶입니다 (2.1.10). i32 | str? 는 i32 | (str?) 입니다.
  • fn() -> i32? 는 optional 을 돌려주는 함수이고, (fn() -> i32)? 는 optional 함수입니다.
  • 타입 자리에서 ?? 토큰은 ? 두 개입니다 (i32??).
  • 식의 x as T? / x is T? 에서 끝의 ? 는 타입에 속합니다. 변환 결과에 ? 연산자 (3.14) 를 쓰려면 (x as T)? 로 괄호를 씁니다. x as T? 의 변환이 실패하면 진단이 "전파하려면 (x as T)? 로 쓰라" 는 힌트를 줍니다.
  • 다만 as / is 의 대상 뒤의 ?? 는 null 합체 연산자입니다 (x as i32 ?? 0 은 (x as i32) ?? 0). 두 층 optional 로 변환하려면 x as (i32??) 로 씁니다.

2.1.6. 함수 타입

함수 자체를 가리키는 타입은 다음과 같이 표기합니다.

fn(i32, i32) -> i32
fn() -> str
fn(T) -> T
fn(i32)                  // 반환 타입 생략 가능 (단위 타입)

함수 타입은 fn 토큰으로 시작하며, 괄호 안에는 매개변수 타입만 적고 이름은 적지 않습니다. 반환 타입은 -> 뒤에 적으며, 이를 생략하면 반환값이 없다는 뜻이 됩니다(정확한 의미는 함수 사양에서 정의합니다).

2.1.7. 튜플 타입

여러 타입을 묶은 튜플 타입은 다음과 같이 표기합니다.

(i32, str)
(f64, f64, f64)
(i32,)                  // 한 항짜리 튜플은 후행 콤마 필수
()                      // 빈 튜플 (단위 타입)

튜플 타입에는 몇 가지 규칙이 적용됩니다. 우선 항이 둘 이상이면 콤마로 구분합니다. 그리고 항이 하나인 튜플은 반드시 후행 콤마((T,)) 를 붙여야 하며, 이를 빠뜨리면 그냥 T 의 그룹화로 해석됩니다. 마지막으로 항이 없는 튜플(()) 은 단위 타입을 가리킵니다.

2.1.8. 배열 타입

고정 크기 배열을 나타내는 타입은 대괄호로 표기합니다.

[T; N]
[i32; 16]
[f64; 1024]
[u8; SIZE]

배열 타입은 대괄호 안에 원소 타입, 세미콜론, 길이 순으로 적습니다. 길이는 const 표현식이며, 컴파일 타임에 결정되어야 합니다. 그리고 길이가 0 인 경우([T; 0]. 빈 배열 타입) 도 허용됩니다.

2.1.9. 동적 슬롯

동적인 타입 슬롯을 나타낼 때는 any 키워드를 사용합니다.

any
any Error
any (Display + Hash)
any Iterator<Item = i32>
any Future<Output = User>

any 는 키워드 에 예약된 타입 이름 슬롯 키워드이며, 그 의미는 any 슬롯 에서 정의합니다. any 뒤에 트레잇 경로 하나, 또는 괄호로 묶어 + 로 이은 트레잇 경로들을 붙이면 그 트레잇을 구현하는 값만 담는 제약 붙은 동적 슬롯이 됩니다 (2.2.10). 트레잇 경로의 <...> 안에는 타입 인자뿐 아니라 Item = T 처럼 associated type 을 고정하는 바인딩도 올 수 있습니다 (trait_arg). 트레잇 경로가 둘 이상이면 괄호가 필요하며, any 는 바로 뒤의 트레잇 하나만 묶으므로 any Error | none 은 (any Error) | none 입니다. 트레잇 경로는 any 뒤에서만 타입 자리에 올 수 있습니다 (트레잇-as-타입 금지).

2.1.10. 우선순위 / 결합

타입 표현식 안에서 결합자가 섞일 때 우선순위(강한 결합 ↔ 약한 결합) 는 다음과 같습니다.

strong  ─→  weak

[T; N]   (...)   <...>   ::   .  →  T?  →  fn(...) -> ...   →   |

이를 풀어 설명하면, 경로(::), 제네릭 적용(<...>), 배열([...]), 그룹화((...)) 가 가장 강하게 묶입니다. 그 다음이 후위 optional(?), 그 다음으로 함수 타입(fn(...) -> ...) 이 묶이고, 합 타입(|) 이 가장 약하게 묶입니다.

그 결과로 i32 | List<f32> 는 i32 | (List<f32>) 로, fn(A | B) -> C 는 fn((A|B)) -> C 로 묶입니다.

만약 명시적인 그룹화가 필요하다면 괄호로 감싸면 됩니다.

fn(i32) -> (i32 | str)         // 반환 타입이 합 타입
(fn(i32) -> i32) | (fn(f64) -> f64)   // 합 타입이 두 함수 타입을 항으로

GitHub에서 이 페이지 고치기