개요
추가적인 메타데이터와 능력들도 가지고 있는 포인터
참조자가 데이터를 오직 빌리기만 하는 포인터
스마트 포인터는 그들이 가리키고 있는 데이터를 소유
Deref, DerefMut와 Drop 트레잇을 구현한 구조체를 이용하여 구현
Deref
인스턴스가 참조자처럼 동작하게 해줌
역참조 강제(deref coercion)
Deref를 구현한 어떤 타입의 참조자를 Deref가 본래의 타입으로부터 바꿀 수 있는 타입의 참조자로 변경
불변 참조자에 대한 *를 오버 라이딩 가능
DerefMut
Drop
인스턴스가 스코프 밖으로 벗어났을 때 실행되는 코드를 오버 라이딩 가능
std::mem::drop 함수를 이용하여 수동 drop 가능
Box
데이터를 힙에 저장
사용 예시
컴파일 타임에 크기를 알 수 없는 타입(재귀적 타입 (recursive type))을 사이즈를 알아야하는 로직에 이용하고 싶을 경우
소유권을 옮길 때 복사가 일어나지 않음을 보장받고 싶을 경우
특정 트레잇을 구현한 타입이라는 점만 신경 쓰고 싶을 경우
Rc
참조 카운팅 (reference counting) 의 약자
복수 소유자를 갖는 것이 가능
어떤 값이 계속 사용되는지 혹은 그렇지 않은지를 알기 위해 해당 값에 대한 참조자의 갯수를 계속 추적
단일 스레드 시나리오 상에서만 사용 가능
Rc::clone 함수
호출할 때 참조 카운트 증가
러스트의 관례는 a.clone() 보다 Rc::clone(&a)를 이용
깊은 복사 종류의 클론과 참조 카운트를 증가시키는 종류의 클론을 시각적으로 구별 가능
Rc::strong_count 함수
RefCell
내부 가변성 패턴을 따르는 타입
내부 가변성 (interior mutability)
어떤 데이터에 대한 불변 참조자가 있을 때라도 데이터를 변경할 수 있게 해주는 러스트의 디자인 패턴
빌림 규칙에 의해 허용되지 않으므로 데이터 구조 내에서 unsafe (안전하지 않은) 코드를 사용
런타임에 빌림 규칙을 따를 것임을 보장할 수 있다면, 컴파일러가 이를 보장하지 못하더라도 내부 가변성 패턴을 이용하는 타입 사용 가능
unsafe 코드는 안전한 API로 감싸져 있고, 외부 타입은 여전히 불변이므로 컴파일 통과
런타임에 빌림 규칙에 어긋나면 패닉 발생
Rc와는 다르게 단일 소유자을 지님
단일 스레드 시나리오 상에서만 사용 가능
빌림 규칙을 따르는 것을 확신하지만, 컴파일러는 이를 이해하고 보장할 수 없을 경우 유용
Rc와의 조합을 자주 사용
Rc<RefCell<T>>
복수 소유자를 갖으면서 값 변경이 가능
Weak
약한 참조(weak reference)
순환 참조 방지에 자주 쓰임
Rc::clone 함수 대신 Rc::downgrade 함수를 호출
Weak 타입의 스마트 포인터 반환
weak_count를 1 증가
weak_count가 0이 아니여도 strong_count가 0이면 인스턴스 제거
upgrade 메소드
Rc::weak_count 함수
Arc
예제 1
코드
use std :: ops :: Deref ;
struct MyBox < T > ( T );
impl < T > MyBox < T > {
fn new ( x : T ) -> MyBox < T > {
MyBox ( x )
}
}
impl < T > Deref for MyBox < T > {
type Target = T ;
fn deref ( & self ) -> & T {
& self .0
}
}
fn func ( s : & str ) {
println! ( "{}" , s );
}
fn main () {
println! ( "1.1 : {}" , Box :: new ( 1 ));
println! ( "1.2 : {}" , * Box :: new ( 1 ));
println! ( "1.3 : {}" , & Box :: new ( 1 ));
println! ( "1.4 : {}" , * MyBox :: new ( 1 ));
func ( "2.1 : a" );
func ( & Box :: new ( String :: from ( "2.2 : b" )));
func ( & MyBox :: new ( String :: from ( "2.3 : c" )));
}
실행 결과
1.1 : 1
1.2 : 1
1.3 : 1
1.4 : 1
2.1 : a
2.2 : b
2.3 : c
예제 2
코드
#[derive(Debug)]
struct Test {
s : String ,
}
impl Drop for Test {
fn drop ( & mut self ) {
println! ( "drop call - {}" , self .s );
}
}
fn main () {
{
let test1 = Test {
s : String :: from ( "1.1 : a" ),
};
let test2 = Test {
s : String :: from ( "1.2 : b" ),
};
println! ( "1.3 : {:?}, {:?}" , test1 , test2 );
}
{
let test1 = Test {
s : String :: from ( "2.1 : a" ),
};
let test2 = Test {
s : String :: from ( "2.2 : b" ),
};
println! ( "2.3 : {:?}, {:?}" , test1 , test2 );
drop ( test1 );
}
}
실행 결과
1.3 : Test { s: "1.1 : a" } , Test { s: "1.2 : b" }
drop call - 1.2 : b
drop call - 1.1 : a
2.3 : Test { s: "2.1 : a" } , Test { s: "2.2 : b" }
drop call - 2.1 : a
drop call - 2.2 : b
예제 3
코드
use std :: rc :: Rc ;
use List ::{ Cons , Nil };
#[derive(Debug)]
enum List {
Cons ( i32 , Rc < List > ),
Nil ,
}
fn main () {
{
let l1 = Rc :: new ( Cons ( 5 , Rc :: new ( Cons ( 10 , Rc :: new ( Nil )))));
println! ( "1.1 : {:?}, {}" , l1 , Rc :: strong_count ( & l1 ));
let l2 = Cons ( 3 , Rc :: clone ( & l1 ));
println! ( "1.2 : {:?}, {}" , l2 , Rc :: strong_count ( & l1 ));
let l3 = Cons ( 4 , Rc :: clone ( & l1 ));
println! ( "1.3 : {:?}, {}" , l3 , Rc :: strong_count ( & l1 ));
}
{
let l1 = Rc :: new ( Cons ( 5 , Rc :: new ( Cons ( 10 , Rc :: new ( Nil )))));
println! ( "2.1 : {:?}, {}" , l1 , Rc :: strong_count ( & l1 ));
{
let l2 = Cons ( 3 , Rc :: clone ( & l1 ));
println! ( "2.2 : {:?}, {}" , l2 , Rc :: strong_count ( & l1 ));
}
let l3 = Cons ( 4 , Rc :: clone ( & l1 ));
println! ( "2.3 : {:?}, {}" , l3 , Rc :: strong_count ( & l1 ));
}
}
실행 결과
1.1 : Cons( 5, Cons( 10, Nil)) , 1
1.2 : Cons( 3, Cons( 5, Cons( 10, Nil))) , 2
1.3 : Cons( 4, Cons( 5, Cons( 10, Nil))) , 3
2.1 : Cons( 5, Cons( 10, Nil)) , 1
2.2 : Cons( 3, Cons( 5, Cons( 10, Nil))) , 2
2.3 : Cons( 4, Cons( 5, Cons( 10, Nil))) , 2
예제 4
코드
use std :: cell :: RefCell ;
fn func_1 ( v : & Vec < String > ) -> usize {
v .len ()
}
fn func_2 ( v : & mut Vec < String > , s : String ) {
v .push ( String :: from ( s ));
}
fn main () {
let ref_cell = RefCell :: new ( vec! []);
println! ( "1 : {}" , ref_cell .borrow () .len ());
ref_cell .borrow_mut () .push ( String :: from ( "a" ));
println! ( "2 : {}" , ref_cell .borrow () .len ());
println! ( "3 : {}" , func_1 ( & ref_cell .borrow ()));
println! ( "4 : {}" , func_1 ( & ref_cell .borrow_mut ()));
func_2 ( & mut ref_cell .borrow_mut (), String :: from ( "b" ));
println! ( "5 : {}" , ref_cell .borrow_mut () .len ());
}
실행 결과
1 : 0
2 : 1
3 : 1
4 : 1
5 : 2
예제 5
코드
use std :: rc :: Rc ;
#[derive(Debug)]
struct Test {
s : String ,
}
impl Drop for Test {
fn drop ( & mut self ) {
println! ( "drop call - {}" , self .s );
}
}
fn main () {
let rc = Rc :: new ( Test {
s : String :: from ( "1.1 : a" ),
});
println! ( "1 : {:?}" , rc );
let weak = Rc :: downgrade ( & rc );
println! ( "2.1 : {}" , Rc :: weak_count ( & rc ));
println! ( "2.2 : {:?}" , weak );
match weak .upgrade () {
Some ( _test ) => println! ( "2.3 : {:?}" , rc ),
None => println! ( "2.3 None" ),
}
let _weak = Rc :: downgrade ( & rc );
println! ( "2.4 : {}" , Rc :: weak_count ( & rc ));
}
실행 결과
1 : Test { s: "1.1 : a" }
2.1 : 1
2.2 : ( Weak)
2.3 : Test { s: "1.1 : a" }
2.4 : 2
drop call - 1.1 : a
Tags:
Arc ,
Box ,
Deref ,
deref coercion ,
DerefMut ,
Drop ,
interior mutability ,
Rc ,
Rc::clone ,
Rc::downgrade ,
Rc::strong_count ,
Rc::weak_count ,
recursive type ,
smart pointers ,
Weak
Categories:
programming-language ,
Rust
Updated: September 13, 2023