부록 C: 파생 가능한 트레이트
이 책의 여러 곳에서 구조체나 열거형 정의에 적용할 수 있는
derive
속성에 대해 설명했습니다. derive
속성은 derive
문법으로 명시한 타입에 대해 자체적인 기본 구현이 있는 트레이트를
구현하는 코드를 생성합니다.
이 부록에서는 derive
와 함께 사용할 수 있는 표준 라이브러리의 모든 트레이트에
대한 참고 자료를 제공합니다. 각 절이 다룰 내용은 다음과 같습니다:
- 이 트레이트를 파생하면 어떤 연산자와 메서드가 활성화되는지
derive
가 제공하는 트레이트의 구현체가 하는 일- 타입에 대해 트레이트를 구현한다는 것의 의미
- 트레이트를 구현할 수 있는 조건 혹은 허용되지 않는 조건
- 트레이트가 필요한 연산들의 예
derive
속성이 제공하는 것과는 다른 동작을 원한다면, 각 트레이트에 대한
표준 라이브러리 문서를 참고하여
수동으로 구현하는 방법에 대한 자세한 내용을 확인하세요.
여기에 나열된 트레이트들은 표준 라이브러리에서 derive
를 사용하여
타입에 구현할 수 있는 유일한 트레이트들입니다. 표준 라이브러리에
정의된 다른 트레이트에는 적절한 기본 동작이 없으므로, 달성하려는
목적에 적합한 방식으로 구현하는 것은 여러분의 몫입니다.
파생될 수 없는 트레이트의 예로는 Display
가 있는데, 이는 최종 사용자를
위한 서식을 처리합니다. 여러분은 최종 사용자에게 타입을 표시할 적절한
방법을 항상 고려해야 합니다. 최종 사용자에게 어떤 부분을 표시해야
할까요? 어떤 부분이 관련성이 있을까요? 어떤 형식의 데이터가 가장
관련성이 높을까요? 러스트 컴파일러에는 이러한 인사이트가 없으므로
적절한 기본 동작을 제공할 수 없습니다.
이 부록에 나열된 트레이트가 파생 가능한 트레이트를 전부 포괄하지는 않습니다:
라이브러리는 자신의 트레이트에 대해 대한 derive
를 구현할 수 있으므로,
derive
를 사용할 수 있는 트레이트 목록은 정말 개방적입니다. derive
를
구현하려면 절차적 매크로를 사용해야 하며, 이는 19장의
‘매크로’절에서 다룹니다.
프로그래머 출력을 위한 Debug
Debug
트레이트는 형식 문자열에서 디버그 서식을 활성화하는데, 이는 {}
자리표시자 내에 :?
를 추가하여 표시합니다.
Debug
트레이트는 디버깅 목적으로 어떤 타입의 인스턴스를 출력할 수 있게
해주므로, 여러분과 여러분의 타입을 사용하는 다른 프로그래머들은 프로그램의
실행 중 특정 지점에서 인스턴스를 검사할 수 있습니다.
Debug
트레이트는 이를테면 assert_eq!
매크로를 사용할 때 필요합니다.
이 매크로는 동등 단언이 실패했을 경우 인스턴스의 값을 출력하여 프로그래머가
두 인스턴스가 같지 않은 이유를 확인할 수 있도록 해 줍니다.
동등 비교를 위한 PartialEq
및 Eq
PartialEq
트레이트는 타입의 인스턴스를 비교하여 동등 여부를 확인하고
==
와 !=
연산자를 사용할 수 있게 해 줍니다.
PartialEq
를 파생시키면 eq
메서드가 구현됩니다. PartialEq
가 구조체에
파생되면, 두 인스턴스는 모든 필드가 동일할 때만 동일하고, 필드 중 어느
하나라도 동일하지 않다면 두 인스턴스는 동일하지 않습니다. 열거형에 파생되면,
각 배리언트는 자기 자신과는 동일하고 다른 변형과는 동일하지 않습니다.
예를 들면 PartialEq
트레이트는 assert_eq!
매크로를 사용할 때 필요한데,
이 매크로는 두 인스턴스를 비교하여 동등 여부를 확인할 수 있어야 하기
때문입니다.
Eq
트레이트에는 메서드가 없습니다. 이 트레이트의 목적은 어노테이션된 타입의
모든 값에 대해 해당 값이 자기 자신과 동일하다는 것을 나타내는 것입니다. Eq
트레이트는 PartialEq
를 구현한 모든 타입에 적용할 수 있지만, PartialEq
를
구현한 모든 타입이 Eq
를 구현할 수 있는 것은 아닙니다. 이에 대한 한 가지
예는 부동 소수점 숫자 타입입니다: 부동 소수점 숫자 구현은 not-a-number(NaN
)
값의 두 인스턴스가 서로 동일하지 않다고 명시합니다.
Eq
가 필요한 예시로는 HashMap<K, V>
의 키로서, HashMap<K, V>
가 두
키가 동일한지 여부를 알 수 있도록 하는 것입니다.
순서 비교를 위한 PartialOrd
및 Ord
PartialOrd
트레이트는 정렬 목적을 위하여 타입의 인스턴스를 비교할 수 있게
해줍니다. PartialOrd
를 구현한 타입은 <
, >
, <=
, >=
연산자를
사용할 수 있습니다. PartialOrd
트레이트는 PartialEq
를 구현한 타입에만
적용할 수 있습니다.
PartialOrd
트레이트를 파생시키면 partial_cmp
메서드가 구현되는데, 이는
Option<Ordering>
을 반환하며, 반환 값은 주어진 값이 순서를 정의하지 않을
때 None
이 됩니다. 이 트레이트를 구현한 타입의 대부분의 값은 비교할 수
있지만, 순서를 정의하지 않는 값의 예가 있다면 not-a-number (NaN
) 부동
소수점 값입니다. NaN
부동 소수점 숫자와 어떤 부동 소수점 숫자를 사용하여
partial_cmp
를 호출하면 None
이 반환될 것입니다.
구조체에 대해 파생되면, PartialOrd
는 구조체 정의에서 필드가 나타나는
순서대로 각 필드의 값을 비교하는 방식으로 두 인스턴스를 비교합니다.
열거형에 대해 파생되면, 열거형 정의에서 먼저 선언된 배리언트는 나중에
선언된 배리언트보다 작다고 간주됩니다.
예를 들면 PartialOrd
트레이트는 범위 표현식에 의해 지정된 범위
내에서 임의의 값을 생성하는 rand
크레이트의 gen_range
메서드를
사용할 때 필요합니다.
Ord
트레이트는 어떤 두 값에 대해 항상 유효한 순서가 존재한다는 것을
알려줍니다. Ord
트레이트는 cmp
메서드를 구현하는데, 이는 Option<Ordering>
이 아닌 Ordering
을 반환합니다. 이는 항상 유효한 순서가 존재하기 때문입니다.
Ord
트레이트는 PartialOrd
와 Eq
를 구현한 타입에만 적용할 수 있습니다.
(Eq
는 PartialEq
를 필요로 합니다.) 구조체와 열거형에 대해 파생되면,
cmp
는 PartialOrd
의 partial_cmp
에 대한 파생 구현체가 동작하는
방식과 동일하게 동작합니다.
Ord
가 필요한 한 가지 예는 BTreeSet<T>
에 값을 저장할 때인데, 이는
값의 정렬 순서에 따라 데이터를 저장하는 데이터 구조입니다.
값을 복제하기 위한 Clone
과 Copy
Clone
트레이트는 명시적으로 값의 깊은 복사 (deep copy) 를 생성할 수 있게
해주며, 복제 과정은 임의의 코드를 실행하고 힙 데이터를 복사할 수도 있습니다.
Clone
에 대한 더 자세한 내용은 4장의 ‘변수와 데이터 간 상호작용 방식:
클론 (clone)’
절을 참고하세요.
Clone
을 파생하면 clone
메서드가 구현되는데, 이는 타입 전체에 대해 구현될
때 타입의 각 부분에 대해 clone
을 호출합니다. 이는 Clone
을 파생하기 위해서는
타입의 모든 필드나 값 또한 Clone
을 구현해야 한다는 것을 의미합니다.
Clone
이 필요한 경우의 한 예에는 슬라이스에 to_vec
메서드를 호출할 때가
있습니다. 슬라이스는 자신이 가진 타입 인스턴스를 소유하지 않지만, to_vec
에서
반환된 벡터는 자신의 인스턴스를 소유해야 하므로, to_vec
은 각 아이템에 대해
clone
을 호출합니다. 따라서 슬라이스에 저장된 타입은 Clone
을 구현해야 합니다.
Copy
트레이트는 임의의 코드 없이 스택에 저장된 비트만 복사하여 값을
복제할 수 있게 해 줍니다. Copy
에 대한 더 자세한 내용은 4장의
‘스택에만 저장되는 데이터: 복사 (copy)’절을
참고하세요.
Copy
트레이트에는 아무 메서드도 정의되어 있지 않은데, 이는 프로그래머가
메서드를 오버로딩하고 임의의 코드를 실행하지 않는다는 가정을 위반하는 것을
방지하기 위해서입니다. 따라서 모든 프로그래머는 복사가 매우 빠르게 수행될
것이라고 가정할 수 있습니다.
어떤 타입에 대해 Copy
를 파생하려면 그 타입의 모든 부분이 Copy
를 구현해야
합니다. Copy
를 구현하는 타입은 또한 Clone
을 구현해야 하는데, 왜냐하면 Copy
를
구현하는 타입은 Copy
와 동일한 작업을 수행하는 Clone
의 단순한 구현체를 가지고
있기 때문입니다.
Copy
트레이트는 드물게만 요구됩니다; Copy
를 구현하는 타입은
최적화가 가능하므로 clone
을 호출할 필요가 없으며, 이는 코드를 더
간결하게 만듭니다.
Copy
를 사용하여 가능한 모든 것은 Clone
을 사용하여 수행할 수 있지만, 코드가
느려지거나 clone
을 사용해야 하는 경우가 있을 수 있습니다.
어떤 값을 고정 크기의 값으로 매핑하기 위한 Hash
Hash
트레이트는 해시 함수를 사용하여 임의의 크기를 가진 타입의 인스턴스를
고정 크기의 값으로 매핑할 수 있게 해 줍니다. Hash
를 파생하면 hash
메서드가
구현됩니다. 파생된 hash
메서드의 구현체는 타입의 각 부분에 대해 hash
를
호출한 결과를 조합하는데, 이는 Hash
를 파생하기 위해서는 타입의 모든 필드
혹은 값 또한 Hash
를 구현해야 한다는 것을 의미합니다.
Hash
를 필요로 하는 한 가지 예는 효율적으로 데이터를 저장하기 위하여
HashMap<K, V>
에 키를 저장할 때입니다.
기본값을 위한 Default
Default
트레이트는 타입에 대한 기본값을 생성할 수 있게 해 줍니다. Default
를
파생하면 default
함수가 구현됩니다. 파생된 default
함수의 구현체는
타입의 각 부분에 대해 default
를 호출하는데, 이는 Default
를 파생하기
위해서는 타입의 모든 필드 혹은 값 또한 Default
를 구현해야 한다는 것을
의미합니다.
Default::default
함수는 일반적으로 5장의
‘기존 인스턴스를 이용해 새 인스턴스를 만들 때 구조체 업데이트 문법 사용하기’절에서
다룬 구조체 업데이트 구문과 함께 사용됩니다.
구조체의 몇 개의 필드만 커스터마이징한 다음
나머지 필드에 대해 기본값을 설정하고 사용하려면
..Default::default()
를 사용할 수
있습니다.
Default
트레이트는 예를 들면 Option<T>
인스턴스에서 unwrap_or_default
메서드를 사용할 때 필요합니다. Option<T>
가 None
이면, unwrap_or_default
메서드는 Option<T>
에 저장되는 T
타입에 대한 Default::default
의 결과를
반환합니다.