콘텐츠로 이동
Xen / docs

문서 검색

한국어 · v0.1.0-demo.2

검색어를 입력하세요.

    EN
    Xen / learn v0.1.0-demo.2

    제네릭과 타입 추론

    값을 담거나 전달하는 코드를 여러 타입에 사용하려면 <T>로 타입 매개변수를 선언합니다. 다음을 generics.xen으로 저장하세요.

    struct Pair<T> { first: T, second: T }
    fn identity<T>(value: T) -> T {
    return value;
    }
    fn main() {
    let pair = Pair<Int> { first: identity(7), second: 9 };
    println(pair.first);
    println(identity<String>("hello"));
    }
    터미널 창
    $ xen run generics.xen
    7
    hello

    identity(7)에서는 인자의 타입으로 T를 결정합니다. identity<String>("hello")는 타입을 직접 지정합니다. 여러 타입이 있으면 <A, B>처럼 모두 적습니다. 구조체와 enum도 타입 매개변수를 가질 수 있습니다. 제네릭 타입의 메서드는 impl<T> Pair<T> { ... } 형태로 선언합니다.

    빈 벡터나 Option.None은 그것만으로 항목 타입을 알 수 없습니다. let items: Vec<Int> = [];, let missing: Option<String> = Option.None;처럼 적으세요.

    제네릭 함수의 타입 추론은 인자 타입을 사용하며, 결과를 받을 변수의 타입으로 결정하지 않습니다. let n: I32 = identity(1);은 인자 1로부터 Int를 선택하므로 실패합니다. identity<I32>(1) 또는 identity(i32(1))로 바꾸세요. 타입 인자를 일부만 적는 방식은 지원하지 않습니다.

    제네릭 함수는 값을 보관·전달·반환하고 이미 모양이 알려진 Option<T>, Vec<T> 등을 다룰 수 있습니다. 그러나 아무 타입이나 뜻하는 T에 바로 +, 비교, 임의의 필드나 메서드를 사용할 수는 없습니다. 예를 들어 fn add<T>(a: T, b: T) -> T 안에서 a + b는 오류입니다. 숫자 계산에는 Int 같은 구체적인 타입을 쓰거나, 필요한 작업을 수행하는 이름 있는 함수를 콜백으로 전달하세요. trait·제약·연산자 오버로딩은 지원하지 않습니다.

    이미 알려진 enum이나 튜플은 제네릭 함수 안에서도 match로 나눌 수 있습니다. 아직 타입을 모르는 T를 숫자 리터럴이나 특정 enum으로 구분할 수는 없습니다.

    T가 어떤 타입인지 몰라도 Option<T>의 Some·None은 구분할 수 있습니다. 다음을 generic_shapes.xen으로 저장하세요. or_default는 숫자 연산 없이 값을 골라 반환하고, Pair<T>의 메서드는 선언된 필드를 읽습니다.

    generic_shapes.xen
    struct Pair<T> { first: T, second: T }
    impl<T> Pair<T> {
    fn left(&self) -> T {
    return self.first;
    }
    }
    fn or_default<T>(value: Option<T>, fallback: T) -> T {
    return match value {
    Option.Some(item) => item,
    Option.None => fallback,
    };
    }
    fn main() {
    let pair = Pair<Int> { first: 7, second: 9 };
    println(pair.left());
    let missing: Option<String> = Option.None;
    println(or_default(missing, "untitled"));
    println(or_default(Option.Some(42), 0));
    }
    터미널 창
    xen check generic_shapes.xen
    xen run generic_shapes.xen
    generic_shapes.stdout
    7
    untitled
    42

    or_default(missing, "untitled")에서는 두 인자가 String이라는 같은 T를 사용합니다. Some이면 안의 값을, None이면 기본값을 반환합니다. 기본값은 함수 호출 전에 평가됩니다. 계산 비용이나 부수 효과 때문에 기본값 평가를 피해야 한다면 호출 위치에서 match로 분기하세요.

    반환 타입만으로 추론하지 않음

    섹션 제목: “반환 타입만으로 추론하지 않음”

    다음은 의도적인 오류입니다. xen check generic_inference_error.xen으로 확인하세요.

    generic_inference_error.xen
    fn identity<T>(value: T) -> T { return value; }
    fn main() { let value: I32 = identity(1); println(value); }
    generic_inference_error.stderr
    expected I32, found Int (I64)

    identity(1)은 인자 1에서 Int를 선택합니다. 왼쪽의 I32가 이 선택을 바꾸지는 않습니다. identity<I32>(1)로 타입 인자를 지정하거나 identity(i32(1))로 인자 타입을 먼저 바꾸세요.

    타입 매개변수만으로는 연산을 정할 수 없음

    섹션 제목: “타입 매개변수만으로는 연산을 정할 수 없음”

    다음도 의도적인 오류입니다. xen check generic_add_error.xen으로 확인하세요.

    generic_add_error.xen
    fn add<T>(left: T, right: T) -> T {
    return left + right;
    }
    fn main() { println(add(1, 2)); }
    generic_add_error.stderr
    operation requires a concrete type

    T는 정수일 수도 있고, 덧셈이 정의되지 않은 타입일 수도 있습니다. 이 릴리스는 trait·타입 제약·연산자 오버로딩을 제공하지 않습니다. 정수 덧셈이 목적이면 매개변수를 Int로 선언하세요. 변환 작업을 바꿔 끼우려면 슬라이스와 이터레이터처럼 이름 있는 구체적 함수를 콜백으로 넘길 수 있습니다.

    제네릭으로 선언했다고 값의 복사·이동 규칙이 없어지지는 않습니다. 실제 타입의 동작은 복사·이동·참조를 확인하세요.

    오류 예제의 identity(1)을 두 가지 방법으로 고쳐 보세요. or_default에 서로 다른 타입의 인자를 주었을 때 하나의 T로 처리할 수 없는 이유도 설명해 보세요.