특별한 타입과 트레잇

특별한 타입과 트레잇 (Special Types and Traits)

Rust의 표준 라이브러리 중에는 일반 사용자 타입과는 다른 '특별한 대우'를 받는 타입·트레잇이 있어요. 그걸 컴파일러가 알고 있고, 코드 작성 시에만 허용되는 문법이나 자동 구현이 붙는 경우가 많습니다. 이 문서가 바로 그 특별한 기능들을 정리해 둔 장입니다.

출처: Rust Reference

표준 라이브러리에 있는 특정 타입과 트레잇은 Rust 컴파일러가 알고 있습니다. 이 장은 이런 타입·트레잇들이 가진 특별한 기능들을 다룹니다.

Box<T>

Box<T>에는 Rust가 아직 사용자 정의 타입에서 허용하지 않는 몇 가지 특별한 기능이 있습니다.

  • Box<T>에 대한 역참조 연산자(dereference operator)이동(move)할 수 있는 place를 만들어냅니다. 즉 * 연산자와 Box<T>의 소멸자가 언어 자체에 내장되어 있다는 뜻이에요.
  • 메서드Box<Self>를 수신자(receiver)로 받을 수 있습니다.
  • T가 정의된 크레이트 안에서 Box<T>에 대해 트레잇을 구현할 수 있는데, 이는 orphan 규칙이 다른 제네릭 타입에는 금지하는 동작입니다.

Rc<T>

메서드Rc<Self>를 수신자로 받을 수 있습니다.

Arc<T>

메서드Arc<Self>를 수신자로 받을 수 있습니다.

Pin<P>

메서드Pin<P>를 수신자로 받을 수 있습니다.

UnsafeCell<T>

std::cell::UnsafeCell<T>내부 가변성(interior mutability)을 위해 사용됩니다. 컴파일러가 이런 타입에 대해 올바르지 않은 최적화를 수행하지 않도록 보장해요.

또한 내부 가변성을 가진 타입의 static 아이템이 읽기 전용으로 표시된 메모리에 배치되지 않도록 보장합니다.

PhantomData<T>

std::marker::PhantomData<T>zero-sized이며 최소 정렬(minimum alignment)을 가진 타입으로, 분산(variance), drop check, auto trait 목적상 T를 소유한 것으로 간주됩니다.

연산자 트레잇 (Operator Traits)

std::opsstd::cmp의 트레잇들은 연산자, 인덱싱 표현식, 호출 표현식을 오버로드하는 데 사용됩니다.

DerefDerefMut

단항 * 연산자를 오버로드하는 것 외에도, DerefDerefMut메서드 해석deref 강제(coercion)에서도 사용됩니다.

Drop

Drop 트레잇은 이 타입의 값이 파괴될 때마다 실행될 소멸자(destructor)를 제공합니다.

Copy

Copy 트레잇은 이를 구현하는 타입의 의미론을 바꿉니다.

타입이 Copy를 구현하면, 그 타입의 값은 대입 시 이동(move)이 아니라 복사(copy)됩니다.

CopyDrop을 구현하지 않고, 모든 필드가 Copy인 타입에만 구현할 수 있어요. enum이라면 모든 variant의 모든 필드가 Copy여야 하고, union이라면 모든 variant가 Copy여야 합니다.

Copy는 컴파일러가 다음 타입들에 대해 구현합니다:

Clone

Clone 트레잇은 Copy의 수퍼트레잇이므로, 역시 컴파일러가 생성한 구현이 필요합니다.

컴파일러는 다음 타입들에 대해 구현합니다:

  • 내장 Copy 구현이 있는 타입 (위 참고)
  • 튜플Clone 타입의 튜플
  • Clone 타입의 값만 캡처하거나 환경에서 아무 값도 캡처하지 않는 클로저

Send

Send 트레잇은 이 타입의 값을 한 스레드에서 다른 스레드로 안전하게 보낼 수 있음을 나타냅니다.

Sync

Sync 트레잇은 이 타입의 값을 여러 스레드 사이에서 안전하게 공유할 수 있음을 나타냅니다.

이 트레잇은 불변(immutable) static 아이템에 사용되는 모든 타입에 대해 구현되어야 합니다.

Termination

Termination 트레잇은 main 함수테스트 함수의 허용 가능한 반환 타입을 나타냅니다.

Auto Trait

Send, Sync, Unpin, UnwindSafe, RefUnwindSafe 트레잇은 auto trait입니다. auto trait은 특별한 성질을 가져요.

주어진 타입에 대해 auto trait의 명시적 구현이나 부정(negative) 구현이 작성되지 않았다면, 컴파일러가 다음 규칙에 따라 자동으로 구현합니다:

  • &T, &mut T, *const T, *mut T, [T; n], [T]T가 구현하면 그 트레잇을 구현합니다.
  • 함수 아이템 타입과 함수 포인터는 트레잇을 자동으로 구현합니다.
  • struct, enum, union, 튜플은 모든 필드가 구현하면 트레잇을 구현합니다.
  • 클로저는 모든 캡처의 타입이 구현하면 트레잇을 구현합니다. T를 공유 참조로, U를 값으로 캡처하는 클로저는 &TU 둘 다가 구현하는 모든 auto trait을 구현해요.

제네릭 타입(위 내장 타입들을 T에 대해 제네릭으로 센 경우)에서, 제네릭 구현이 가능하다면 컴파일러는 그 구현을 사용할 수 있지만 필수 트레잇 바운드를 충족하지 못하는 타입에는 자동으로 구현하지 않습니다. 예를 들어 표준 라이브러리는 모든 &T(TSync)에 대해 Send를 구현하는데, 이는 TSend이지만 Sync가 아닌 경우 컴파일러가 &T에 대해 Send를 구현하지 않음을 뜻합니다.

Auto trait은 부정 구현도 가질 수 있습니다. 표준 라이브러리 문서에서 impl !AutoTrait for T처럼 표시되며, 자동 구현을 덮어씁니다. 예를 들어 *mut TSend의 부정 구현을 가지므로, TSend여도 *mut TSend가 아니에요. 추가 부정 구현을 지정하는 안정적인 방법은 아직 없으며, 표준 라이브러리에만 존재합니다.

Auto trait은 보통 하나의 트레잇만 허용되는 트레잇 객체에 추가 바운드로 붙일 수 있습니다. 예를 들어 Box<dyn Debug + Send + UnwindSafe>는 유효한 타입이에요.

Sized

Sized 트레잇은 이 타입의 크기가 컴파일 타임에 알려져 있음을 나타냅니다. 즉 동적 크기 타입(dynamically sized type)이 아니라는 뜻이에요.

타입 매개변수는 (트레잇 안의 Self를 제외하고) 기본적으로 Sized이며, 연관 타입도 마찬가지입니다.

Sized는 항상 구현 아이템이 아니라 컴파일러가 자동으로 구현합니다.

이런 암묵적인 Sized 바운드는 특별한 ?Sized 바운드로 완화할 수 있습니다.

더 알아보기 (Learn more)