Box 와 Rc를 사용해 동적 메모리를 할당 받을 수 있다.
1. Box
- Box::new : 메모리 할당
- * : 메모리에 접근
fn main() {
let mut x = Box::new(10); //Box를 사용하여 힙에 데이터를 할당
println!("x: {}", x); // x: 10
*x = 20; // 할당받은 메모리에 접근 하려면 * 사용
println!("x: {}", x); // x: 20
}
2. Rc(Reference-counting pointer) : 강한 참조
- Rc로 관리되는 데이터는 공유가 가능해서 여러 변수가 동일한 값을 참조할 수 있다.
- 데이터를 참조하는 모든 변수들이 메모리에 존재하면 값을 해체 하지 않는다.
- 참조 횟수를 추적해 참조 횟수가 0이 되면 데이터를 삭제한다.
- 순환 참조가 발생하면 참조 횟수가 0이 되지 않아서 메모리 누수 발생한다.
use std::rc::Rc;
struct Person {
age: i32,
}
fn main() {
// person을 공유 객체로 생성
let person = Rc::new(Person { age: 10 });
// person복제
let p1 = person.clone();
println!("person: {} p1: {}", person.age, p1.age);
println!("RefCount: {}", Rc::strong_count(&person)); //+1
// person복제
let p2 = person.clone();
println!("RefCount: {}", Rc::strong_count(&person)); //+1
{ //새로운 스코프 시작
// person복제
let p3 = person.clone();
println!("RefCount: {}", Rc::strong_count(&person)); //+1
} //스코프 종료 (그래서 p3는 소멸됨)
println!("RefCount: {}", Rc::strong_count(&person)); //-1
}
3. RefCell
- Rc는 불변성을 가진 참조형이기에 공유 데이터를 변경할 수 없다.
- 공유 데이터를 수정하려고 하면 컴파일 오류가 발생한다.
- RefCell 은 변경 불가능한 변수를 임시로 변경 가능하게 해준다.
// RefCell<T>를 사용해 next 의 참조값을 변경하는 예제
use std::rc::Rc;
use std::cell::RefCell; //내부 가변성을 제공
struct Person {
name: String,
age: i32,
next: RefCell<Option<Rc<Person>>>, // RefCell<>로 감싸서 next는 수정 가능
}
fn main() {
// p1 노드 생성
let mut p1 = Rc::new(Person {
name: String::from("Luna"),
age: 30,
next: RefCell::new(None), //처음에는 다음 노드가 없음.
});
// p2 노드 생성
let mut p2 = Rc::new(Person {
name: String::from("Rust"),
age: 10,
next: RefCell::new(None), //처음에는 다음 노드가 없음.
});
// p1의 next 필드에 대한 가변 참조를 얻음
let mut next = p1.next.borrow_mut();
// * 키워드를 사용하면 RefCell<T>의 내부 값을 얻을 수 있다.
*next = Some(p2.clone()); // p1뒤에 p2를 추가
}
4. 약한 참조
- Rc는 순환참조가 발생하면 메모리 누수가 발생한다.
- Weak 를 사용하면 약한 참조를 만들어서 순환 참조 문제를 해결 가능하다.
// Rc와 Weak를 사용해서 순환 참조 문제를 해결하는 예제
use std::rc::{Rc, Weak}; // Rc: 강한 참조 / Weak: 약한 참조
use std::cell::RefCell; // 내부 가변성을 위해 사용 (가변 borrow)
struct Person {
id: i32, // Person 식별자 (id)
next: RefCell<Option<Weak<Person>>>, // 다음 사람 참조 (약한 참조)
// Weak<T> 를 사용하면 참조카운트를 증가시키지 않음
}
// Rc 참조카운트가 0이 되면 자동 호출되는 drop 함수 구현
impl Drop for Person {
fn drop(&mut self) {
// 메모리 해제될 때 어떤 Person이 drop 되었는지 출력
println!("p{} Drop!", self.id);
}
}
fn main() {
// p1 객체 생성 (참조카운트 strong=1)
let mut p1 = Rc::new(Person {
id: 1,
next: RefCell::new(None),
});
// p2 객체 생성 (참조카운트 strong=1)
let mut p2 = Rc::new(Person {
id: 2,
next: RefCell::new(None),
});
// p1 -> p2 연결 (약한 참조로 연결)
{
let mut next = p1.next.borrow_mut(); // RefCell을 사용해 내부 가변 접근
*next = Some(Rc::downgrade(&p2)); // Rc::downgrade() : Weak 참조 생성
// Rc::downgrade를 사용하면 참조카운트 증가하지 않음 (순환참조 해결)
}
// p2 -> p1 연결 (약한 참조로 연결)
{
let mut next = p2.next.borrow_mut();
*next = Some(Rc::downgrade(&p1)); // 약한 참조로 연결
}
// 현재 Rc 참조카운트 출력
println!("p1 RefCount: {} p2: RefCount: {}",
Rc::strong_count(&p1), Rc::strong_count(&p2));
// 출력 예시
// p1 RefCount: 1 p2 RefCount: 1
// 약한 참조는 strong_count 증가 안되므로
// main 스코프 종료시 Rc 카운트 1 -> 0 으로 떨어짐
// -> Drop 호출됨
}
Box 와 Rc
- 외부 공유가 필요한 경우 Rc 사용, 그렇지 않다면 Box 사용
- 수정이 불가능한 Rc변수를 변경하려면 RefCell 기법도 사용
댓글 없음:
댓글 쓰기