Today
-
Yesterday
-
Total
-
  • Rust Struct의 비일관성이 아쉽다.
    Practice/C, Rust 2019.03.06 12:34

    구조체 종류별로 정의와 생성 그리고 필드에 접근하는 예제부터 보자.

    - Named-field Struct: 우리가 다른 언어에서 많이 접했던 바로 그 구조체

    struct S { x: f32, y: f32 }
    let s = S { x: 120.0, y: 209.0 };
    let var = s.x;

    - Unit-like Struct: 필드 없는 구조체

    struct E;
    let e = E;

    - Tuple-like Struct: 필드의 이름 대신에 순서로 구분하는 구조체

    struct T(i32, char);
    let t = T(120, 'x');
    let var = t.1;


    이 중에서 tuple-like struct의 구조체 이름은 함수처럼 사용할 수 있다.
    아래 예제를 보자.

    #[derive(Debug)]
    struct TupleStruct(u32);
    
    fn make_struct<F>(v: u32, f: F) -> TupleStruct
        where F: Fn(u32) -> TupleStruct
    {
        f(v)
    }
    
    fn main() {
        let t = make_struct(23, TupleStruct);
        println!("{:?}", t);
    }

    make_struct 함수의 두번째 인자(parameter) 타입이 "Fn(u32) -> TupleStruct"로 함수를 받는다.
    여기에는 'fn'으로 정의한 함수명이나 closure가 들어가는 것이 보통이다.
    위 예에서는 'TupleStruct'라는 tuple-like struct의 이름을 인수(argument)로 전달했다.
    바꿔 말하면, 'TupleStruct'라는 이름을 함수로 사용했다.


    다른 구조체(named-field struct나 unit-like struct)는 이와 같은 기능이 없다.
    오직 tuple-like struct의 이름만 예외적으로 함수의 지위를 부여받았다.


    여기서 내가 싫어하는 "예외"라는 단어가 나온다.
    A에 대응하는 A'가 있으면, A와 유사한 B에도 B'가 있어야 한다.
    그렇지 않으면 A'와 B' 모두 없는 것이 좋다.
    일관성은 기억하고 사용하기 쉽게 해주기 때문이다.
    그래서 예외는 참 받아들이기 싫다.


    하지만, 이미 1.0으로 나왔고 널리 사용되는 방식이라고 한다.
    되돌리기에는 늦었다.
    눈 딱 감고 받아들이는 수밖에 없다.


    한국 러스트 사용자 모임에서 이 얘기를 꺼냈더니 다음과 같은 답변을 들었다.

    @sanxiyn

    ML이나 Haskell에서 함수 처리하니까 함수인 것이고
    무슨 깊은 뜻은 없습니다

    @Kroisse

    그거 아마 문법 정할 당시에도 이견이 많았던 걸로 기억하는데


    이견이 많았으면 좀 천천히 도입했으면 어땠을까 하는 아쉬움이 든다.


    비일관성의 또 다른 예가 더 있다.
    그것은 구조체 정의 끝에 오는 세미콜론(;)의 문제다.
    이 글의 시작에 보여준 예제코드를 유심히 봤다면 알 수 있다.
    Named-field Struct는 정의 끝에는 세미콜론을 붙이면 안된다.
    그 외의 구조체는 세미콜론을 반드시 붙여야 한다.

    struct S { x: f32, y: f32 }    // 세미콜론을 붙이면 오류가 난다
    struct E;
    struct T(i32, char);


    모든 대괄호 끝에 세미콜론을 붙이지 않는다면 예외가 아닐 수 있다.
    예를 들면, 함수 정의나 코드 블럭의 대괄호 끝에는 세미콜론이 없듯이 말이다.
    그런데 아쉽게도, named-field struct 생성에는 세미콜론이 붙는다.[각주:1]
    즉, 대괄호와 세미콜론의 일관성도 없다.

    #[derive(Debug)]
    struct S { x: f32, y: f32 }
    
    fn add(x: f32, y: f32) -> f32 {
        let s = S { x, y };    // 반환값이 아니면 세미콜론을 반드시 붙인다
        {
            println!("{:?}", s);
        }
        s.x + s.y
    }


    일관되지 않는 사례를 다 외워야 할까?
    다행이도 CDD 방식이 있어서 꼭 암기할 필요는 없다.
    Compiler Driven Development. :-)

    1. 반환(return)으로 사용될 때는 세미콜론을 붙이지 않는다. 이는 모든 것에 해당함으로 대괄호와 상관없다. [본문으로]

    댓글 0

Designed by Tistory.