포인터 타입(Pointer types)

포인터 타입(Pointer types)

모든 포인터는 명시적인 일급 값(first-class value)이에요. 이동하거나 복사할 수 있고, 데이터 구조에 저장할 수 있으며, 함수에서 반환할 수도 있죠.

출처: Rust Reference

본문

참조(&&mut)

문법부터 볼게요.

ReferenceType → & Lifetime? mut? TypeNoBounds

공유 참조(&)

공유 참조(shared reference)는 다른 어떤 값이 소유한 메모리를 가리켜요.

값에 대한 공유 참조를 만들면 그 값을 직접 변경하지 못하게 막아요. 내부 가변성(interior mutability)이 특정 상황에서 예외를 제공하죠. 이름이 말해주듯, 값에 대한 공유 참조는 몇 개든 존재할 수 있어요. 공유 참조 타입은 &type으로 쓰고, 명시적 수명이 필요하면 &'a type으로 써요.

참조를 복사하는 건 "얕은(shallow)" 연산이에요. 포인터 자체만 복사되는 거죠. 다시 말해 포인터는 Copy예요. 참조를 해제해도 가리키는 값에는 영향이 없지만, 임시 값을 참조하면 참조 자체의 스코프 동안 그 임시 값을 살려 둬요.

가변 참조(&mut)

가변 참조(mutable reference)는 다른 어떤 값이 소유한 메모리를 가리켜요. 가변 참조 타입은 &mut type 또는 &'a mut type으로 써요.

가변 참조(빌려지지 않은)는 가리키는 값에 접근하는 유일한 방법이라서 Copy가 아니에요.

원시 포인터(*const*mut)

문법을 볼게요.

RawPointerType → * ( mut | const ) TypeNoBounds

원시 포인터(raw pointer)는 안전성이나 활성(liveness) 보장이 없는 포인터예요. *const T 또는 *mut T로 쓰죠. 예를 들어 *const i32는 32비트 정수를 가리키는 원시 포인터를 뜻해요.

원시 포인터를 복사하거나 떨어뜨리는(drop) 것은 다른 어떤 값의 수명에도 영향을 주지 않아요. 원시 포인터를 역참조하는 것은 unsafe 연산이에요.

이를 이용해 원시 포인터를 재빌림(reborrow)해서 참조로 변환할 수도 있어요(&* 또는 &mut *). 다만 원시 포인터는 일반적으로 권장되지 않아요. 외부 코드와의 상호운용성(interoperability)을 지원하고, 성능이 중요한 또는 저수준 함수를 작성하기 위해 존재하는 거예요.

원시 포인터를 비교할 때는 가리키는 대상이 아니라 주소로 비교해요. 동적으로 크기가 정해지는 타입(dynamically sized type)을 가리키는 원시 포인터를 비교할 때는 추가 데이터도 함께 비교해요.

원시 포인터는 *const 포인터에는 &raw const를, *mut 포인터에는 &raw mut를 써서 직접 만들 수 있어요.

스마트 포인터

표준 라이브러리에는 참조와 원시 포인터 너머의 추가적인 '스마트 포인터' 타입들이 있어요.

비트 유효성(Bit validity)

대부분의 플랫폼에서 생성되는 기계어에서 포인터와 참조가 usize와 비슷하긴 하지만, 참조나 포인터 타입을 비포인터 타입으로 transmute하는 의미(semantics)는 현재 결정되지 않았어요. 그래서 포인터나 참조 타입 P[u8; size_of::<P>()]로 transmute하는 건 유효하지 않을 수 있어요.

얇은(thin) 원시 포인터(즉 T: Sized에 대해 P = *const T 또는 P = *mut T)에 대해서는 반대 방향(정수나 정수 배열에서 P로 transmute)은 항상 유효해요. 그러나 그런 transmute로 만들어진 포인터는 역참조할 수 없어요(설령 T의 크기가 0인 ZST라 하더라도).

더 알아보기

  • 수명과 참조 세부 규칙
  • unsafe 연산과 내부 가변성
  • 크기 미정(unsized) 타입과 동적 크기 타입
  • transmute와 타입 변환의 유효성
  • 외부 코드와의 상호운용성