관련 유형과 제네릭 유형을 사용하는 것이 적절한시기는 언제입니까?


109

에서 이 질문 , 문제는 그 연관된 유형에 제네릭 형식 매개 변수를 사용을 시도 변경하여 해결할 수 일어났다. 이로 인해 "여기서 관련 유형이 더 적합한 이유는 무엇입니까?"라는 질문을하게되었고 더 많은 것을 알고 싶었습니다.

관련 유형을 도입RFC 는 다음 같이 말합니다.

이 RFC는 다음을 통해 특성 일치를 명확히합니다.

  • 모든 형질 타입 파라미터 치료 입력 유형
  • 출력 유형 인 연관된 유형 제공 .

RFC는 동기를 부여하는 예제로 그래프 구조를 사용 하며 문서 에서도 사용 되지만 유형 매개 변수화 된 버전에 비해 관련 유형 버전의 이점을 충분히 이해하지 못함을 인정합니다. 가장 중요한 것은 distance메서드가 Edge유형 에 대해 신경 쓸 필요가 없다는 것입니다 . 이것은 좋지만 관련 유형을 갖는 이유가 조금 얕아 보입니다.

관련 유형이 실제로 사용하기에 매우 직관적이라는 것을 알았지 만 내 API에서 언제 어디서 사용해야할지 결정할 때 어려움을 겪고 있습니다.

코드를 작성할 때 제네릭 유형 매개 변수보다 연관된 유형을 언제 선택해야하고 언제 반대해야합니까?

답변:


75

이것은 이제 The Rust Programming Language 의 두 번째 판 에서 다루어 졌습니다. 그러나 추가로 잠수 해 보겠습니다.

더 간단한 예부터 시작하겠습니다.

특성 방법을 사용하는 것이 언제 적절합니까?

후기 바인딩 을 제공하는 방법에는 여러 가지가 있습니다 .

trait MyTrait {
    fn hello_word(&self) -> String;
}

또는:

struct MyTrait<T> {
    t: T,
    hello_world: fn(&T) -> String,
}

impl<T> MyTrait<T> {
    fn new(t: T, hello_world: fn(&T) -> String) -> MyTrait<T>;

    fn hello_world(&self) -> String {
        (self.hello_world)(self.t)
    }
}

모든 구현 / 성능 전략을 무시하고 위의 두 발췌 부분을 통해 사용자 hello_world는 어떻게 행동해야하는지 동적으로 지정할 수 있습니다.

한 차이 (의미)가 있다는 것이다 trait주어진 유형의 것을 구현 보장 T구현은 trait, hello_world반면 항상 동일한 동작을한다 struct구현이 인스턴스별로 다른 동작을 갖는 수있다.

방법 사용이 적절한 지 여부는 사용 사례에 따라 다릅니다!

연관된 유형을 사용하는 것이 적절한시기는 언제입니까?

trait위 의 방법과 유사하게 , 연관된 유형은 후기 바인딩의 한 형태이며 (컴파일시 발생하지만)의 사용자 trait가 대체 할 유형을 지정된 인스턴스에 지정할 수 있도록합니다 . 이것이 유일한 방법은 아닙니다 (따라서 질문).

trait MyTrait {
    type Return;
    fn hello_world(&self) -> Self::Return;
}

또는:

trait MyTrait<Return> {
    fn hello_world(&Self) -> Return;
}

위의 메서드의 후기 바인딩과 동일합니다.

  • 첫 번째는 주어진 Self하나의 Return관련
  • 두 번째는, 대신, 실행 허용 MyTrait을 위해 Self여러 대Return

어떤 형식이 더 적절한 지 여부는 단일성을 시행하는 것이 합당한 지 여부에 따라 다릅니다. 예를 들면 :

  • Deref 유니시티가 없으면 컴파일러가 추론 중에 미쳐 버릴 것이기 때문에 연관된 유형을 사용합니다.
  • Add 저자는 두 개의 인수가 주어지면 논리적 반환 유형이 있다고 생각했기 때문에 연관된 유형을 사용합니다.

당신이 볼 수 있듯이, 동안 Deref이다 명백한 유스 케이스 (기술적 제약)의 경우 Add덜 명확한입니다 : 어쩌면에 대한 의미 만들 것 i32 + i32하나 양보 할 수 i32또는 Complex<i32>상황에 따라? 그럼에도 불구하고 저자는 판단을 내렸고 추가를 위해 반환 유형을 오버로드 할 필요가 없다고 결정했습니다.

제 개인적인 입장은 정답이 없다는 것입니다. 그럼에도 불구하고, 단일성 인수를 넘어서, 연관된 유형은 지정해야하는 매개 변수의 수를 줄임으로써 특성을 사용하는 것을 더 쉽게 만든다고 언급하고 싶습니다. 따라서 일반 특성 매개 변수를 사용하는 유연성의 이점이 명확하지 않은 경우에 대비하여 관련 유형으로 시작하는 것이 좋습니다.


4
조금 단순화 해 보겠습니다 : trait/struct MyTrait/MyStruct정확히 하나 impl MyTrait for또는 impl MyStruct. 일반적이기 때문에 trait MyTrait<Return>여러를 허용합니다 impl. Return모든 유형이 될 수 있습니다. 일반 구조체는 동일합니다.
Paul-Sebastian Manole

2
당신의 대답은 "The Rust Programming Language"에있는 것보다 훨씬 더 이해하기 쉽습니다
drojf

"첫 번째는 주어진 Self에 대해 하나의 리턴이 연관되도록 강제합니다." 이것은 즉각적인 의미에서 사실이지만 물론 일반적인 특성으로 서브 클래 싱함으로써이 제한을 극복 할 수 있습니다. 아마도 unicity는 제안 일 뿐이며 강제되지는 않습니다.
joel

37

관련 유형은 그룹화 메커니즘 이므로 유형을 함께 그룹화하는 것이 합리적 일 때 사용해야합니다.

Graph문서에 소개 된 특성은이의 예입니다. 는 Graph일반화 되기를 원 하지만 특정 종류의을 갖게 Graph되면 Node또는 Edge유형이 더 이상 변경 되는 것을 원하지 않습니다. 특정 사람 Graph은 단일 구현 내에서 이러한 유형을 변경하고 싶지 않으며 실제로 항상 동일하기를 원합니다. 그들은 함께 그룹화되거나 심지어 연관 이라고 말할 수 있습니다 .


5
이해하는데 시간이 좀 걸렸습니다. 나에게 그것은 한 번에 여러 유형을 정의하는 것과 비슷해 보입니다. Edge와 Node는 그래프에서 의미가 없습니다.
tafia
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.