15.0 Smart Pointers

Smart pointers, on the other hand, are data structures that act like a pointer but also have additional metadata and capabilities. This pointer enables you to allow data to have multiple owners by keeping track of the number of owners and, when no owners remain, cleaning up the data.

  • burrow ์‹œ์Šคํ…œ๊ณผ ์—ฐ๊ด€ํ•ด์„œ ๋ด์•ผํ•˜๋Š”๊ฒƒ๋“ค์ด ์žˆ๋‹ค.
    • ์ฐธ์กฐ๋Š” ๋ฐ์ดํ„ฐ๋ฅผ burrow ํ•ด์ฃผ์ง€๋งŒ,
    • smart pointer๋Š” ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ์†Œ์œ ํ•œ๋‹ค.
Aspect Reference (&T) Smart Pointer (Box<T>, etc.)
Ownership Borrows only Owns the data
Memory Allocation Stack (points to stack or heap) Usually heap
Lifetimes Tied to scope of borrow Can live beyond original owner
Mutability Rules Strict compile-time rules Some allow runtime mutability
Use Case Fast, safe temporary access Complex ownership/sharing models

Though we didnโ€™t call them as such at the time, weโ€™ve already encountered a few smart pointers in this book, includingย Stringย andย Vec<>ย in Chapter 8. Both these types count as smart pointers because they own some memory and allow you to manipulate it. They also have metadata and extra capabilities or guarantees.ย String, for example, stores its capacity as metadata and has the extra ability to ensure its data will always be valid UTF-8.

  • ์œ„ ๋‚ด์šฉ์„ ์š”์•ฝํ•˜๋ฉด, ๋ฉ”๋ชจ๋ฆฌ(ํž™์˜)๋ฅผ ์†Œ์œ ํ•˜๊ณ , ์กฐ์ž‘ํ•  ๊ธฐ๋Šฅ์ด ์žˆ๋‹ค๋ฉด ์Šค๋งˆํŠธํฌ์ธํ„ฐ์™€ ๋ณธ์งˆ์ ์œผ๋กœ ๋‹ค๋ฅด์ง€ ์•Š๋‹ค๋Š” ๊ฒƒ, ๋Œ€ํ‘œ์ ์œผ๋กœ ๋ฌธ์ž์—ด๊ณผ ๋ฒกํ„ฐ๊ฐ™์€ ๊ฒƒ๋“ค์ด ์žˆ๋‹ค๊ณ  ๋งํ•œ๋‹ค.
  • ๊ฒฐ๋ก ์€ ํž™ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์†Œ์œ ํ•˜๋Š” ๊ตฌ์กฐ์ฒด์ผ ๋ฟ์ด์ง€๋งŒ ๋ช‡๊ฐ€์ง€๋ฅผ ๋” ๊ตฌํ˜„๊ฒƒ
    • ๋ช‡๊ฐ€์ง€์˜ ์˜ˆ์‹œ๋Š” Deref, Drop๋“ฑ์ด ์žˆ๋‹ค.
    • Deref์€ ์ฐธ์กฐ์ฒ˜๋Ÿผ ๋™์ž‘ํ•˜๊ธฐ ์œ„ํ•œ ๊ธฐ๋Šฅ์„ ์ •์˜ํ•œ ๊ฒƒ ์ด๊ณ ,
    • Drop์€ ๊ตฌ์กฐ์ฒด ๋ฉ”๋ชจ๋ฆฌ ์ •๋ฆฌ๋ฅผ ์œ„ํ•ด์„œ ๊ตฌํ˜„ํ•ด์•ผํ•œ๋‹ค.

์Šค๋งˆํŠธ ํฌ์ธํ„ฐ๋Š” ๊ฒฐ๊ตญ โ€œ์–ด๋–ป๊ฒŒ ์†Œ์œ ํ•˜๊ณ , ์–ธ์ œ ํ•ด์ œํ•˜๋ฉฐ, ๋ˆ„๊ฐ€ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ• ์ง€โ€์— ๋Œ€ํ•œ ์ฑ…์ž„์„ ๊ฐ€์ง€๋Š” ๊ตฌ์กฐ์ฒด์ด์ž ๊ทธ ๋ชฉ์  ์ž์ฒด๋ฅผ ์œ„ํ•ด์„œ ์„ค๊ณ„๋œ ๊ตฌ์กฐ์ฒด์ด๋‹ค.

์ด ์žฅ์—์„œ ๋‹ค๋ฃฐ ํ‘œ์ค€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ๋Œ€ํ‘œ์ ์ธ ์Šค๋งˆํŠธ ํฌ์ธํ„ฐ๋“ค

  1. Box
    • ๊ฐ’์„ ํž™(Heap)์— ์ €์žฅํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•ด์คŒ
  2. Rc
    • ์ฐธ์กฐ ์นด์šดํŒ… ๊ธฐ๋ฐ˜ ์†Œ์œ ๊ถŒ: ์—ฌ๋Ÿฌ ์†Œ์œ ์ž๊ฐ€ ํ•˜๋‚˜์˜ ๋ฐ์ดํ„ฐ๋ฅผ ๊ณต์œ ํ•  ์ˆ˜ ์žˆ์Œ
  3. RefCell
    • Ref์™€ RefMut๋ฅผ ํ†ตํ•ด ์ ‘๊ทผ
    • ๋Ÿฐํƒ€์ž„์— Burrow checker๋ฅผ ์ˆ˜ํ–‰ (์ปดํŒŒ์ผ ํƒ€์ž„์ด ์•„๋‹Œ)

์ถ”๊ฐ€๋กœ ๋‹ค๋ฃฐ ๊ฐœ๋…

  • Interior Mutability (๋‚ด๋ถ€ ๊ฐ€๋ณ€์„ฑ): ์™ธ๋ถ€์ ์œผ๋กœ๋Š” ๋ถˆ๋ณ€์ฒ˜๋Ÿผ ๋ณด์—ฌ๋„, ๋‚ด๋ถ€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€๋ณ€์ ์œผ๋กœ ์กฐ์ž‘ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ—ˆ์šฉํ•˜๋Š” ํŒจํ„ด
  • ์ˆœํ™˜ ์ฐธ์กฐ(Reference Cycles):
  • Rc ๋“ฑ์„ ์‚ฌ์šฉํ•  ๋•Œ ์ž˜๋ชปํ•˜๋ฉด ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Œ
  • ์ด๋ฅผ ์˜ˆ๋ฐฉํ•˜๋Š” ๋ฐฉ๋ฒ•๋„ ์„ค๋ช…ํ•จ

15.1 Using Box to Point to Data on the Heap

Boxes donโ€™t have performance overhead, other than storing their data on the heap instead of on the stack. But they donโ€™t have many extra capabilities either. Youโ€™ll use them most often in these situations:

  • Box๋Š” ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ์—†์Œ์—๋„ ๋‹ค์–‘ํ•œ ๊ธฐ๋Šฅ๋“ค์„ ์ œ๊ณตํ•œ๋‹ค.
  • ์•„๋ž˜๋Š” ๊ฐ€์žฅ ๋Œ€ํ‘œ์ ์ธ ์˜ˆ์‹œ๋“ค

์ปดํŒŒ์ผ ํƒ€์ž„์— ํฌ๊ธฐ๋ฅผ ์•Œ ์ˆ˜ ์—†๋Š” ํƒ€์ž…์„ ์‚ฌ์šฉํ•ด์•ผ ํ•˜์ง€๋งŒ, ์ •ํ™•ํ•œ ํฌ๊ธฐ๋ฅผ ์š”๊ตฌํ•˜๋Š”๊ณณ์— ์จ์•ผ ํ•  ์ƒํ™ฉ

  • ๋Œ€ํ‘œ์ ์œผ๋กœ ์žฌ๊ท€์ ์ธ ์ž๋ฃŒ๊ตฌ์กฐ
enum List {
    Cons(i32, List), // โŒ ์ปดํŒŒ์ผ ์—๋Ÿฌ ๋ฐœ์ƒ
    Nil,
}

fn main() {
    let list = Cons(1, Cons(2, Cons(3, Nil)));
}
error[E0072]: recursive type `List` has infinite size
  • ์žฌ๊ท€์ ์œผ๋กœ ๋์—†์ด ํ™•์žฅ๋  ์ˆ˜ ์žˆ์–ด์„œ ์ปดํŒŒ์ผ ์‹œ ๊ณ„์‚ฐํ•  ์ˆ˜ ์—†๋‹ค.
enum List {
ย  ย  Cons(i32, &mut List), // or &List
ย  ย  Nil,

}
  • ์ด๋Ÿฐ ๋ฐฉ๋ฒ•๋„ ๋  ๊ฒƒ ๊ฐ™์ง€๋งŒ ์‚ฌ์‹ค ๋‘๊ฐ€์ง€ ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค.
    • ์†Œ์œ ๊ถŒ์ด ์•„๋‹ˆ๋ผ ์ฐธ์กฐ๋ฅผ ๋„˜๊ธฐ๊ธฐ ๋•Œ๋ฌธ์— ์›๋ณธ ๋ณ€์ˆ˜์˜ ๋ผ์ดํ”„ํƒ€์ž„์„ ์•Œ ์ˆ˜ ์—†๊ณ 
    • ์†Œ์œ ํ•˜๋Š”๊ฒŒ ์•„๋‹˜์œผ๋กœ ๋ฐœ์ƒํ•˜๋Š” ๋ฌธ์ œ๋“ค์ด ์žˆ๋‹ค.
```rust
enum List {
    Cons(i32, Box<List>), // โœ… Box๋ฅผ ์‚ฌ์šฉํ•ด ๊ณ ์ • ํฌ๊ธฐ ํฌ์ธํ„ฐ๋กœ ๋งŒ๋“ฆ
    Nil,
}

fn main() {
    let list = List::Cons(1, Box::new(
                List::Cons(2, Box::new(
                List::Cons(3, Box::new(List::Nil)))));
}
  • ์œ„์ฒ˜๋Ÿผ Box๋ฅผ ํ†ตํ•ด ํ•ด๊ฒฐ
    • ๊ณ ์ •ํฌ๊ธฐ ํฌ์ธํ„ฐ๋กœ ์ปดํŒŒ์ผํƒ€์ž„์— ๊ฐ’์˜ ํฌ๊ธฐ๋ฅผ ์ถ”๋ก ํ•  ์ˆ˜ ์žˆ๊ณ 
    • ํ•ด๋‹น ํž™์— ์ €์žฅ๋œ ๋ฐ์ดํ„ฐ์˜ ์†Œ์œ ๊ถŒ์„ enum์ด ๊ฐ€์ง€๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค.

ํฐ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณต์‚ฌํ•˜์ง€ ์•Š๊ณ  ๋„˜๊ธฐ๊ณ  ์‹ถ์„ ๋•Œ (Box๋กœ move๋งŒ ํ•˜๊ณ  ์‹ค์ œ ๋ฐ์ดํ„ฐ๋Š” ๊ทธ๋Œ€๋กœ)

fn process(data: Box<[u8; 1000000]>) {
    println!("์ฒ˜๋ฆฌ ์™„๋ฃŒ");
}

fn main() {
    let big = Box::new([0u8; 1000000]); // ์‹ค์ œ ๋ฐ์ดํ„ฐ๋Š” heap์—
    process(big); // ํฌ์ธํ„ฐ๋งŒ move๋จ, ๋ณต์‚ฌ ์—†์Œ
}
  • ์ •ํ™•ํžˆ๋Š” ์†Œ์œ ๊ถŒ์„ ‘์ด๋™์‹œํ‚ค๋ฉฐ’ ๊ฐ’์„ ๋„˜๊ธฐ๊ณ ์‹ถ์„๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์†Œ์œ ๊ถŒ์„ ๊ฐ€์ง€๊ณ  ์˜ค๋ฏ€๋กœ ํ•จ์ˆ˜ ๋‚ด์—์„œ ์ž์œ ๋กญ๊ฒŒ ์‚ฌ์šฉ๊ฐ€๋Šฅํ•˜๋ฉฐ owner์˜ ์ˆ˜๋ช… ์ œ์•ฝ๋„ ์ƒ๊ด€์—†์ด ์“ธ ์ˆ˜ ์žˆ๋‹ค

ํƒ€์ž…์€ ๋ชจ๋ฅด๊ณ , ํŠธ๋ ˆ์ž‡๋งŒ ์ค‘์š”ํ•  ๋•Œ

trait Draw {
    fn draw(&self);
}

struct Button;
struct Checkbox;

impl Draw for Button {
    fn draw(&self) { println!("Button ๊ทธ๋ฆฌ๊ธฐ"); }
}
impl Draw for Checkbox {
    fn draw(&self) { println!("Checkbox ๊ทธ๋ฆฌ๊ธฐ"); }
}

fn main() {
    let ui: Vec<Box<dyn Draw>> = vec![
        Box::new(Button),
        Box::new(Checkbox),
    ];

    for comp in ui {
        comp.draw();
    }
}

15.1 Treating Smart Pointers Like Reqular References with the Deref Trait

Implementing theย Derefย trait allows you to customize the behavior of theย dereference operatorย *ย (not to be confused with the multiplication or glob operator). By implementingย Derefย in such a way that a smart pointer can be treated like a regular reference, you can write code that operates on references and use that code with smart pointers too.

15.1.1 Following the Pointer to the Value

  • Deref๋ฅผ ๊ตฌํ˜„ํ•˜๋Š” ๊ฒƒ์˜ ๋ชฉ์ ์€ ์—ญ์ฐธ์กฐ ์—ฐ์‚ฐ์ž * ์„ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ์ด๋‹ค.
fn main() {
	let x = 5;
	let y = Box::new(x);

	assert_eq!(5, x);
	assert_eq!(5, *y);
}
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) -> &Self::Target {
        &self.0
    }
}
  • ์œ„์˜ ์ฝ”๋“œ์ฒ˜๋Ÿผ deref์„ ๊ตฌํ˜„ํ•˜๋ฉด ์—ญ์ฐธ์กฐ ์—ฐ์‚ฐ์ž๋ฅผ ์‚ฌ์šฉ ํ•  ์ˆ˜ ์žˆ๋‹ค.

15.1.2 Using Box Like Reference

The reason theย derefย method returns a reference to a value, and that the plain dereference outside the parentheses inย *(y.deref())ย is still necessary, is to do with the ownership system If theย derefย method returned the value directly instead of a reference to the value, the value would be moved out ofย self. We donโ€™t want to take ownership of the inner value insideย MyBox<>ย in this case or in most cases where we use the dereference operator.

  • ๊ฐ’์„ ๋ฐ˜ํ™˜ํ•˜์ง€ ์•Š๊ณ  ์ฐธ์กฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋Š” ์ด์œ ๋Š” ์†Œ์œ ๊ถŒ์ด ๋„˜์–ด๊ฐ€๋Š”๊ฑฐ๊ณ  ๊ทธ๊ฑธ ์›์น˜ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
  • ๊ทธ๋ž˜์„œ ์‹ค์งˆ์ ์œผ๋กœ ์ปดํŒŒ์ผ๋Ÿฌ๋Š” ์—ญ์ฐธ์กฐ ์—ฐ์‚ฐ์ž๋ฅผ ๋งŒ๋‚ฌ์„ ๋•Œ *(y.deref())๋ฅผ ํ˜ธ์ถœํ•ด์ฃผ๋Š” ๊ฒƒ์ด๋‹ค.
  • * ์—ฐ์‚ฐ์ž๋Š” ์šฐ๋ฆฌ๊ฐ€ ์ฝ”๋“œ์—์„œ *๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ๋งˆ๋‹ค, ๋จผ์ € deref ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  ๊ทธ ๊ฒฐ๊ณผ์— ๋Œ€ํ•ด ๋‹ค์‹œ ํ•œ ๋ฒˆ * ์—ฐ์‚ฐ์ž๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ํ•œ ๋ฒˆ๋งŒ ๋Œ€์ฒด๋œ๋‹ค. ์ด * ์—ฐ์‚ฐ์ž์˜ ์น˜ํ™˜(substitution)์€ ๋ฌดํ•œํžˆ ์žฌ๊ท€๋˜์ง€ ์•Š๋Š”๋‹ค.
use std::ops::Deref;

struct MyBox<T>(T);

impl<T> Deref for MyBox<T> {
    type Target = T;
    fn deref(&self) -> &T {
        &self.0
    }
}

fn main() {
    let x = MyBox::new(MyBox::new(5));
    
    // ํ•œ ๋ฒˆ์˜ * => deref() ํ˜ธ์ถœ 1๋ฒˆ
    let inner = *x; // ํƒ€์ž…์€ MyBox<i32>

    // ๋‹ค์‹œ * => deref() ํ˜ธ์ถœ 1๋ฒˆ
    let value = *inner; // ํƒ€์ž…์€ i32

    assert_eq!(value, 5);
}

impl<T> MyBox<T> {
    fn new(x: T) -> Self {
        MyBox(x)
    }
}
  • ์ฆ‰ ์œ„์˜ ์ผ€์ด์Šค์—์„œ๋Š” ๋ช…์‹œ์ ์œผ๋กœ 2๋ฒˆ์˜ ํ˜ธ์ถœ์„ ํ•ด์ค˜์•ผ ์›๋ณธ ๊ฐ’์„ ์–ป์„์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด๋‹ค.

15.1.3 Implicit Deref Coercions with Fuctions and Methods

Deref coercionย converts a reference to a type that implements theย Derefย trait into a reference to another type. For example, deref coercion can convertย &Stringย toย &strย becauseย Stringย implements theย Derefย trait such that it returnsย &str. Deref coercion is a convenience Rust performs on arguments to functions and methods, and works only on types that implement theย Derefย trait. It happens automatically when we pass a reference to a particular typeโ€™s value as an argument to a function or method that doesnโ€™t match the parameter type in the function or method definition. A sequence of calls to theย derefย method converts the type we provided into the type the parameter needs.

  • Deref Coercion์€ ํ•จ์ˆ˜๋‚˜ ๋ฉ”์„œ๋“œ์˜ ์ธ์ž๋กœ ์ „๋‹ฌํ•œ ๊ฐ’๋“ค์ด ๋ถˆ์ผ์น˜ํ•˜๋ฉด์„œ Deref๋ฅผ ๊ตฌํ˜„ํ–ˆ์„ ๋•Œ, ์—ฐ์‡„์ ์ธ ์—ญ์ฐธ์กฐ๋ฅผ ์ž๋™์œผ๋กœ ์ˆ˜ํ–‰ํ•ด์ค˜์„œ ํƒ€์ž…์„ ๋งž์ถฐ์ฃผ๋Š” ๊ธฐ๋Šฅ์ด๋‹ค.
  • ๋Œ€ํ‘œ์ ์œผ๋กœ &String์ด &str๋กœ ๋ณ€ํ™˜๋˜๋Š”๊ฑธ ๋ณผ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด๊ฑด String์ด Deref๋˜์—ˆ์„๋•Œ &str์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก Deref๋ฅผ ๊ตฌํ˜„ํ•ด๋’€๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
// &Box์˜ ์ฐธ์กฐ๋ฅผ ๋„˜๊น€ -> 
// ์•Œ์•„์„œ Deref ํ˜ธ์ถœํ•ด์„œ &String ๋„˜๊น€ ->
// ์•Œ์•„์„œ Deref ํ˜ธ์ถœํ•ด์„œ &str์„ ์•„๊ทœ๋จผํŠธ๋กœ ์ „๋‹ฌ
fn hello(name: &str) {
    println!("Hello, {name}!");
}

fn main() {
    let m = MyBox::new(String::from("Rust"));
    hello(&m);
}




// ๋งŒ์•ฝ Coercions๊ฐ€ ์ผ์–ด๋‚˜์ง€ ์•Š๋Š”๋‹ค๋ฉด
fn main() {
    let m = MyBox::new(String::from("Rust"));
    hello(&(*m)[..]);
}

๋™์ž‘ ๋ฉ”์ปค๋‹ˆ์ฆ˜

  1. ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ํƒ€์ž… ๋ถˆ์ผ์น˜๋ฅผ ๊ฐ์ง€
  2. ์ปดํŒŒ์ผ๋Ÿฌ๋Š” Deref::deref ๋ฉ”์„œ๋“œ๋ฅผ ํ•„์š”ํ•œ ๋งŒํผ ํ˜ธ์ถœํ•˜์—ฌ ๋งค๊ฐœ๋ณ€์ˆ˜ ํƒ€์ž…๊ณผ ์ผ์น˜ํ•˜๋Š” ์ฐธ์กฐ๋ฅผ ์–ป์Œ
  3. ์ด ๊ณผ์ •์€ ์ปดํŒŒ์ผ ์‹œ๊ฐ„์— ํ•ด๊ฒฐ๋˜๋ฏ€๋กœ ๋Ÿฐํƒ€์ž„ ์„ฑ๋Šฅ์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š์Œ

Deref Coercion๊ณผ ๊ฐ€๋ณ€์„ฑ์˜ ์ƒํ˜ธ์ž‘์šฉ

  • Deref Coercion์€ ๋‹ค์Œ ์„ธ ๊ฐ€์ง€ ๊ฒฝ์šฐ์— ์ ์šฉ๋จ:
    1. &T์—์„œ &U๋กœ ๋ณ€ํ™˜ (์กฐ๊ฑด: T: Deref<Target=U>)
      • ๋ถˆ๋ณ€ ์ฐธ์กฐ์—์„œ ๋ถˆ๋ณ€ ์ฐธ์กฐ๋กœ ๋ณ€ํ™˜
      • ์˜ˆ: &String์—์„œ &str๋กœ
    2. &mut T์—์„œ &mut U๋กœ ๋ณ€ํ™˜ (์กฐ๊ฑด: T: DerefMut<Target=U>)
      • ๊ฐ€๋ณ€ ์ฐธ์กฐ์—์„œ ๊ฐ€๋ณ€ ์ฐธ์กฐ๋กœ ๋ณ€ํ™˜
    3. &mut T์—์„œ &U๋กœ ๋ณ€ํ™˜ (์กฐ๊ฑด: T: Deref<Target=U>)
      • ๊ฐ€๋ณ€ ์ฐธ์กฐ์—์„œ ๋ถˆ๋ณ€ ์ฐธ์กฐ๋กœ ๋ณ€ํ™˜
      • ์ด๋Š” ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ๋ฐ˜๋Œ€(๋ถˆ๋ณ€โ†’๊ฐ€๋ณ€)๋Š” ๋ถˆ๊ฐ€๋Šฅ

์„ธ ๋ฒˆ์งธ ๊ฒฝ์šฐ๊ฐ€ ์ค‘์š”ํ•œ ์ด์œ :

  • ๊ฐ€๋ณ€ ์ฐธ์กฐ๋ฅผ ๋ถˆ๋ณ€ ์ฐธ์กฐ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒƒ์€ ์•ˆ์ „ (์ฐจ์šฉ ๊ทœ์น™์„ ์œ„๋ฐ˜ํ•˜์ง€ ์•Š์Œ)
  • ๋ถˆ๋ณ€ ์ฐธ์กฐ๋ฅผ ๊ฐ€๋ณ€ ์ฐธ์กฐ๋กœ ๋ณ€ํ™˜ํ•˜๋Š” ๊ฒƒ์€ ์•ˆ๋จ:
    • ๋Ÿฌ์ŠคํŠธ์˜ ์ฐจ์šฉ ๊ทœ์น™์— ๋”ฐ๋ฅด๋ฉด, ๊ฐ€๋ณ€ ์ฐธ์กฐ๋Š” ํ•ด๋‹น ๋ฐ์ดํ„ฐ์— ๋Œ€ํ•œ ์œ ์ผํ•œ ์ฐธ์กฐ์—ฌ์•ผ ํ•จ
    • ๋ถˆ๋ณ€ ์ฐธ์กฐ๊ฐ€ ์œ ์ผํ•œ ์ฐธ์กฐ๋ผ๋Š” ๋ณด์žฅ์ด ์—†์œผ๋ฏ€๋กœ ๊ฐ€๋ณ€ ์ฐธ์กฐ๋กœ ๋ณ€ํ™˜ํ•  ์ˆ˜ ์—†์Œ

15.2 Running Code on Cleanup with the Drop Trait

Drop ํŠธ๋ ˆ์ดํŠธ๋Š” ์Šค๋งˆํŠธ ํฌ์ธํ„ฐ ํŒจํ„ด์—์„œ ์ค‘์š”ํ•œ ํŠธ๋ ˆ์ดํŠธ๋กœ, ๊ฐ’์ด ์Šค์ฝ”ํ”„๋ฅผ ๋ฒ—์–ด๋‚  ๋•Œ ์ˆ˜ํ–‰ํ•  ๋™์ž‘์„ ์‚ฌ์šฉ์ž๊ฐ€ ์ง์ ‘ ์ •์˜ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์คŒ.

  • ๊ฐ’์ด ์Šค์ฝ”ํ”„๋ฅผ ๋ฒ—์–ด๋‚  ๋•Œ ์ž๋™์œผ๋กœ ์ •๋ฆฌ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰
  • ํŒŒ์ผ ํ•ธ๋“ค, ๋„คํŠธ์›Œํฌ ์—ฐ๊ฒฐ, ๋ฝ๊ณผ ๊ฐ™์€ ๋ฆฌ์†Œ์Šค๋ฅผ ํ•ด์ œํ•˜๋Š” ๋ฐ ์‚ฌ์šฉ
  • ๋ฉ”๋ชจ๋ฆฌ ๋ˆ„์ˆ˜๋ฅผ ๋ฐฉ์ง€ํ•˜๊ณ  ์•ˆ์ „ํ•œ ๋ฆฌ์†Œ์Šค ๊ด€๋ฆฌ๋ฅผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•จ
  • ๋Ÿฌ์ŠคํŠธ์—์„œ๋Š” ํ”„๋กœ๊ทธ๋ž˜๋จธ๊ฐ€ ๋งค๋ฒˆ ๋ฆฌ์†Œ์Šค๋ฅผ ํ•ด์ œํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ง์ ‘ ํ˜ธ์ถœํ•  ํ•„์š”๊ฐ€ ์—†์Œ
  • ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ž๋™์œผ๋กœ ๊ฐ’์ด ์Šค์ฝ”ํ”„๋ฅผ ๋ฒ—์–ด๋‚  ๋•Œ ์ •๋ฆฌ ์ฝ”๋“œ๋ฅผ ์‚ฝ์ž…ํ•จ
  • ์ฝ”๋“œ ์ „์ฒด์— ์ •๋ฆฌ ์ฝ”๋“œ๋ฅผ ๋ช…์‹œ์ ์œผ๋กœ ๋ฐฐ์น˜ํ•  ํ•„์š”๊ฐ€ ์—†์Œ
fn drop(&mut self) {
    // ์ •๋ฆฌ ์ฝ”๋“œ ์ž‘์„ฑ
}
  • ์ด๋ ‡๊ฒŒ drop ๋ฉ”์„œ๋“œ๋งŒ ์ž‘์„ฑํ•˜๋ฉด๋จ
  • ์ฐธ๊ณ ๋กœ drop ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋œ ํ›„์—๋Š” ๋Ÿฌ์ŠคํŠธ ๋Ÿฐํƒ€์ž„์ด ํ•ด๋‹น ๊ฐ’์˜ ๋ฉ”๋ชจ๋ฆฌ๋ฅผ ์ž๋™์œผ๋กœ ํ•ด์ œ.
  • ์ฆ‰, self์˜ ๋ชจ๋“  ํ•„๋“œ๋“ค(๋‚ด๋ถ€์— ์žˆ๋Š” String, Vec ๋“ฑ)๋„ ์ž๋™์œผ๋กœ ์ž์‹ ๋“ค์˜ drop ๋ฉ”์„œ๋“œ๊ฐ€ ํ˜ธ์ถœ๋˜๋ฉฐ ์ •๋ฆฌ๋จ
  • ๊ทธ๋ƒฅ ์œ„์˜ ๊ฐ’์ด ํ•ด์ œ๋˜๊ธฐ์ „์— ์ˆ˜ํ–‰ํ•ด์•ผ ํ•  ์ •๋ฆฌ์ž‘์—…๋งŒ ๋ช…์‹œํ•˜๋ฉด๋จ.
fn main() {
    let c = CustomSmartPointer {
        data: String::from("some data"),
    };
    println!("CustomSmartPointer created.");
    c.drop(); // ์ปดํŒŒ์ผ ์˜ค๋ฅ˜!
    println!("CustomSmartPointer dropped before the end of main.");
}
  • ๋ช…์‹œ์ ์œผ๋กœ ์ง์ ‘ ํ˜ธ์ถœ์€ ๋ง‰ํ˜€์žˆ์Œ
$ cargo run
   Compiling drop-example v0.1.0 (file:///projects/drop-example)
error[E0040]: explicit use of destructor method
  --> src/main.rs:16:7
   |
16 |     c.drop();
   |       ^^^^ explicit destructor calls not allowed
   |
help: consider using `drop` function
   |
16 |     drop(c);
   |     +++++ ~

For more information about this error, try `rustc --explain E0040`.
error: could not compile `drop-example` (bin "drop-example") due to 1 previous error
  • std::mem::drop ์š”๊ฑฐ ํ˜ธ์ถœํ•˜๋ฉด ๋จ

15.3 Rc, the Reference Counted Smart Pointer

In the majority of cases, ownership is clear: you know exactly which variable owns a given value. However, there are cases when a single value might have multiple owners. For example, in graph data structures, multiple edges might point to the same node, and that node is conceptually owned by all of the edges that point to it. A node shouldnโ€™t be cleaned up unless it doesnโ€™t have any edges pointing to it and so has no owners.

๋ชฉ์  :

  • ํ•˜๋‚˜์˜ ๊ฐ’์— ์—ฌ๋Ÿฌ ์†Œ์œ ์ž๊ฐ€ ์žˆ์–ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ ์‚ฌ์šฉ
  • ์ปดํŒŒ์ผ ์‹œ์ ์— ์–ด๋–ค ๋ถ€๋ถ„์ด ๋งˆ์ง€๋ง‰์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ• ์ง€ ๊ฒฐ์ •ํ•  ์ˆ˜ ์—†์„ ๋•Œ ํ™œ์šฉ
  • ๊ฐ’์— ๋Œ€ํ•œ ์ฐธ์กฐ ๊ฐœ์ˆ˜๋ฅผ ์ถ”์ ํ•˜์—ฌ ๋” ์ด์ƒ ์‚ฌ์šฉ๋˜์ง€ ์•Š์„ ๋•Œ ์ •๋ฆฌ

์‚ฌ์šฉ์ด ํ•„์š”ํ•œ ํ™˜๊ฒฝ :

  • ํ”„๋กœ๊ทธ๋žจ์˜ ์—ฌ๋Ÿฌ ๋ถ€๋ถ„์—์„œ ์ฝ์–ด์•ผ ํ•˜๋Š” ๋ฐ์ดํ„ฐ๋ฅผ ํž™์— ํ• ๋‹นํ•  ๋•Œ
  • ์ปดํŒŒ์ผ ์‹œ์ ์— ์–ด๋–ค ๋ถ€๋ถ„์ด ๋งˆ์ง€๋ง‰์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ• ์ง€ ๊ฒฐ์ •ํ•  ์ˆ˜ ์—†์„ ๋•Œ
  • ๋งŒ์•ฝ ๋งˆ์ง€๋ง‰ ์‚ฌ์šฉ์ž๋ฅผ ์•Œ ์ˆ˜ ์žˆ๋‹ค๋ฉด, ์ผ๋ฐ˜์ ์ธ ์†Œ์œ ๊ถŒ ๊ทœ์น™์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ๋” ํšจ์œจ์ 

15.3.1 Using Rc to Share Data

image ์ถœ์ฒ˜ : https://doc.rust-lang.org/book/ch15-04-rc.html#rct-the-reference-counted-smart-pointer

enum List {
    Cons(i32, Box<List>),
    Nil,
}

use crate::List::{Cons, Nil};

fn main() {
    let a = Cons(5, Box::new(Cons(10, Box::new(Nil))));
    let b = Cons(3, Box::new(a));
    let c = Cons(4, Box::new(a));
}
  • ์œ„์™€ ๊ฐ™์ด ๊ตฌํ˜„ํ–ˆ๋‹ค๊ณ  ํ–‡์„ ๋•Œ, ์•„๋ž˜์™€ ๊ฐ™์ด ์ปดํŒŒ์ผ ์—๋Ÿฌ๊ฐ€ ๋‚œ๋‹ค.
$ cargo run
   Compiling cons-list v0.1.0 (file:///projects/cons-list)
error[E0382]: use of moved value: `a`
  --> src/main.rs:11:30
   |
9  |     let a = Cons(5, Box::new(Cons(10, Box::new(Nil))));
   |         - move occurs because `a` has type `List`, which does not implement the `Copy` trait
10 |     let b = Cons(3, Box::new(a));
   |                              - value moved here
11 |     let c = Cons(4, Box::new(a));
   |                              ^ value used here after move

For more information about this error, try `rustc --explain E0382`.
error: could not compile `cons-list` (bin "cons-list") due to 1 previous error
enum List {
    Cons(i32, Rc<List>),
    Nil,
}

use crate::List::{Cons, Nil};
use std::rc::Rc;

fn main() {
    let a = Rc::new(Cons(5, Rc::new(Cons(10, Rc::new(Nil)))));
    let b = Cons(3, Rc::clone(&a));
    let c = Cons(4, Rc::clone(&a));
}
  • ์œ„์™€ ๊ฐ™์ด ํ•ด๊ฒฐ ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋งŒ์•ฝ c ์•ž์— d ๋…ธ๋“œ๋ฅผ ๋ถ™์ด๊ณ  ์‹ถ๋‹ค๋ฉด : let d = Cons(2, Rc::new(c)) ์ฒ˜๋Ÿผํ•˜๋ฉด ๋œ๋‹ค.

Rc::clone(&a) vs a.clone()

  • a.clone()์„ ํ˜ธ์ถœํ•˜๋Š” ๋Œ€์‹  Rc::clone(&a)๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์ปจ๋ฒค์…˜์€ Rc::clone์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ.
  • Rc::clone์˜ ๊ตฌํ˜„์€ clone ๊ตฌํ˜„์ฒ˜๋Ÿผ ๋ชจ๋“  ๋ฐ์ดํ„ฐ์˜ deep copy๋ฅผ ์ˆ˜ํ–‰ํ•˜์ง€ ์•Š์Œ.
  • Rc::clone ํ˜ธ์ถœ์€ ๋‹จ์ง€ ์ฐธ์กฐ ์นด์šดํŠธ๋ฅผ ์ฆ๊ฐ€์‹œํ‚ด
  • Rc::clone์„ ์‚ฌ์šฉํ•จ์œผ๋กœ์จ ๊นŠ์€ ๋ณต์‚ฌ ์ข…๋ฅ˜์˜ ํด๋ก ๊ณผ ์ฐธ์กฐ ์นด์šดํŠธ๋ฅผ ์ฆ๊ฐ€์‹œํ‚ค๋Š” ํด๋ก ์„ ์‹œ๊ฐ์ ์œผ๋กœ ๊ตฌ๋ถ„ ๊ฐ€๋Šฅ.
    • ์ฝ”๋“œ์˜ ์„ฑ๋Šฅ ์ด์Šˆ๋ฅผ ์ฐพ์„ ๋•Œ๋Š” ๊นŠ์€ ๋ณต์‚ฌ ํด๋ก ๋งŒ ๊ณ ๋ คํ•˜๊ณ  Rc::clone ํ˜ธ์ถœ์€ ๋ฌด์‹œํ•  ์ˆ˜ ์žˆ๋‹ค.

15.4 RefCell and the Interial Mutability Pattern

Interior mutability is a design pattern in Rust that allows you to mutate data even when there are immutable references to that data; normally, this action is disallowed by the borrowing rules. To mutate data, the pattern uses unsafe code inside a data structure to bend Rustโ€™s usual rules that govern mutation and borrowing.

  • RefCell<T>์€ ์ปดํŒŒ์ผ ์‹œ๊ฐ„์ด ์•„๋‹Œ ๋Ÿฐํƒ€์ž„์— ๋Œ€์—ฌ ๊ทœ์น™์„ ํ™•์ธํ•˜๋Š” ํƒ€์ž….
  • ์ด๋ฅผ ํ†ตํ•ด ์ผ๋ฐ˜์ ์ธ Rust ๊ทœ์น™์—์„œ๋Š” ๋ถˆ๊ฐ€๋Šฅํ•œ ์ƒํ™ฉ(ํ˜น์€ unsafe ์‚ฌ์šฉํ•ด์•ผํ•˜๋Š” ์ƒํ™ฉ)์—์„œ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.

15.4.1 Enforcing Borrowing Rules at Runtime with RecCell

๋จผ์ € Borrowing check ๋กœ์ง์„ ๋ฆฌ๋งˆ์ธ๋“œํ•˜๋ฉด:

  • ํŠน์ • ์‹œ์ ์—, ํ•˜๋‚˜์˜ ๊ฐ€๋ณ€ ์ฐธ์กฐ ๋˜๋Š” ์—ฌ๋Ÿฌ ๊ฐœ์˜ ๋ถˆ๋ณ€ ์ฐธ์กฐ๋ฅผ ๊ฐ€์งˆ ์ˆ˜ ์žˆ์ง€๋งŒ, ๋‘˜ ๋‹ค๋Š” ์•ˆ๋œ๋‹ค.
  • ์ฐธ์กฐ๋Š” ํ•ญ์ƒ ์œ ํšจํ•ด์•ผ ํ•œ๋‹ค.

๋Œ€์—ฌ ๊ทœ์น™์„ ์ปดํŒŒ์ผ ์‹œ๊ฐ„์— ๊ฒ€์‚ฌํ•˜๋Š” ์žฅ์ ์€ ์˜ค๋ฅ˜๊ฐ€ ๊ฐœ๋ฐœ ๊ณผ์ •์—์„œ ๋” ์ผ์ฐ ๋ฐœ๊ฒฌ๋˜๊ณ , ๋ชจ๋“  ๋ถ„์„์ด ๋ฏธ๋ฆฌ ์™„๋ฃŒ๋˜๊ธฐ ๋•Œ๋ฌธ์— ๋Ÿฐํƒ€์ž„ ์„ฑ๋Šฅ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š๋Š”๋‹ค๋Š” ๊ฒƒ. ์ฆ‰ ์ปดํŒŒ์ผ ์‹œ๊ฐ„์— ๋Œ€์—ฌ ๊ทœ์น™์„ ๊ฒ€์‚ฌํ•˜๋Š” ๊ฒƒ์ด ๋Œ€๋ถ€๋ถ„์˜ ๊ฒฝ์šฐ ์ตœ์„ ์˜ ์„ ํƒ์ด๋ฉฐ, ์ด๊ฒƒ์ด Rust์˜ ๊ธฐ๋ณธ๊ฐ’์ธ ์ด์œ ์ด๋‹ค.

  • ๊ทธ๋ฆฌ๊ณ  ๋Ÿฌ์ŠคํŠธ๋Š” ๋ณดํ†ต ๋ณด์ˆ˜์ ์ธ ๋ฐฉ๋ฒ•์„ ํƒํ•œ๋‹ค.
  • ์ž˜๋ชป๋  ๊ฐ€๋Šฅ์„ฑ์ด ๋‚จ์•„์žˆ๋Š” ์ฝ”๋“œ๋ฅผ ์ปดํŒŒ์ผํ•ด์ค€๋‹ค๋ฉด, ๋Ÿฌ์ŠคํŠธ๋Š” ์‹ ๋ขฐ์„ฑ์„ ์žƒ๋Š”๋‹ค๋Š” ๊ด€๋… ํ•˜์— ์ž‘์„ฑ๋˜์–ด์žˆ๋”ฐ.
  • ์‚ฌ์šฉ์ž๊ฐ€ ๋ถˆํŽธํ•˜๋”๋ผ๋„ ์‹ ๋ขฐ์„ฑ์„ ์ง€ํ‚ค๋Š” ๋””์ž์ธ

๊ฒฐ๋ก ์ ์œผ๋กœ RefCell<T> ํƒ€์ž…์€ ์ฝ”๋“œ๊ฐ€ ๋Œ€์—ฌ ๊ทœ์น™์„ ๋”ฐ๋ฅด์ง€๋งŒ ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ด๋ฅผ ์ดํ•ดํ•˜๊ณ  ๋ณด์žฅํ•  ์ˆ˜ ์—†์„ ๋•Œ ์‚ฌ์šฉํ•œ๋‹ค.

ํ•œ ๋ฒˆ ๋‹ค์‹œ ์š”์•ฝํ•˜๋ฉด :

  • Rc<T>๋Š” ๋™์ผํ•œ ๋ฐ์ดํ„ฐ์˜ ๋‹ค์ค‘ ์†Œ์œ ์ž๋ฅผ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•œ๋‹ค.
    • Box<T>์™€ RefCell<T>๋Š” ๋‹จ์ผ ์†Œ์œ ์ž๋ฅผ ๊ฐ€์ง„๋‹ค.
  • Box<T>๋Š” ์ปดํŒŒ์ผ ์‹œ๊ฐ„์— ๊ฒ€์‚ฌ๋˜๋Š” ๋ถˆ๋ณ€ ๋˜๋Š” ๊ฐ€๋ณ€ ๋Œ€์—ฌ๋ฅผ ํ—ˆ์šฉํ•œ๋‹ค.
    • Rc<T>๋Š” ์ปดํŒŒ์ผ ์‹œ๊ฐ„์— ๊ฒ€์‚ฌ๋˜๋Š” ๋ถˆ๋ณ€ ๋Œ€์—ฌ๋งŒ ํ—ˆ์šฉํ•œ๋‹ค.
    • RefCell<T>๋Š” ๋Ÿฐํƒ€์ž„์— ๊ฒ€์‚ฌ๋˜๋Š” ๋ถˆ๋ณ€ ๋˜๋Š” ๊ฐ€๋ณ€ ๋Œ€์—ฌ๋ฅผ ํ—ˆ์šฉํ•œ๋‹ค.
  • RefCell<T>์€ ๋Ÿฐํƒ€์ž„์— ๊ฒ€์‚ฌ๋˜๋Š” ๊ฐ€๋ณ€ ๋Œ€์—ฌ๋ฅผ ํ—ˆ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์—, RefCell<T>์ด ๋ถˆ๋ณ€์ด๋”๋ผ๋„ RefCell<T> ๋‚ด๋ถ€์˜ ๊ฐ’์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ๋‹ค.

15.4.2 Interior Mutability: A Mutable Borrow to an Immutable Value

๋‹ค์‹œ ๋Œ์•„์™€์„œ RecCell์ด ํ•ด๊ฒฐํ•ด์•ผ ํ•˜๋Š” ์ƒํ™ฉ์„ ๋ณธ๋‹ค

fn main() {
    let x = 5;
    let y = &mut x;
}
$ cargo run
   Compiling borrowing v0.1.0 (file:///projects/borrowing)
error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
 --> src/main.rs:3:13
  |
3 |     let y = &mut x;
  |             ^^^^^^ cannot borrow as mutable
  |
help: consider changing this to be mutable
  |
2 |     let mut x = 5;
  |         +++

For more information about this error, try `rustc --explain E0596`.
error: could not compile `borrowing` (bin "borrowing") due to 1 previous error
  • ๊ฐ’์ด ๋ถˆ๋ณ€์œผ๋กœ ๋ณด์ด๋ฉด์„œ๋„ ๋ฉ”์„œ๋“œ ๋‚ด์—์„œ ์ž์‹ ์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์œผ๋ฉด ์œ ์šฉํ•œ ์ƒํ™ฉ์ด ์žˆ๋‹ค.
  • ์ปดํŒŒ์ผ๋Ÿฌ์˜ ๋Œ€์—ฌ ๊ฒ€์‚ฌ๊ธฐ๊ฐ€ ์ด ๋‚ด๋ถ€ ๊ฐ€๋ณ€์„ฑ์„ ํ—ˆ์šฉํ•˜๊ณ , ๋Œ€์—ฌ ๊ทœ์น™์€ ๋Ÿฐํƒ€์ž„์— ๊ฒ€์‚ฌ๋œ๋‹ค.
  • ๊ทœ์น™์„ ์œ„๋ฐ˜ํ•˜๋ฉด ์ปดํŒŒ์ผ๋Ÿฌ ์˜ค๋ฅ˜ ๋Œ€์‹  panic!์ด ๋ฐœ์ƒํ•˜๊ฒŒ ํ•œ๋‹ค.

15.4.3 A Use Case for Interior Mutability: Mock Objects

์ผ๋‹จ ๊ทธ๋Ÿฌํ•œ ๋‚ด๋ถ€๊ฐ€๋ณ€์„ฑ์ด ํ•„์š”ํ•œ ์ƒํ™ฉ์„ ๋จผ์ € ์ •์˜ํ•œ๋‹ค:

  • ํŠน์ • ๋™์ž‘์„ ๊ด€์ฐฐํ•˜๊ณ  ๊ทธ ๋™์ž‘์ด ์ œ๋Œ€๋กœ ๊ตฌํ˜„๋˜์—ˆ๋Š”์ง€ ๊ฒ€์ฆํ•˜๊ธฐ ์œ„ํ•ด ๋ชจ์˜ ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ์ƒํ™ฉ
pub trait Messenger {
    fn send(&self, msg: &str);
}

pub struct LimitTracker<'a, T: Messenger> {
    messenger: &'a T,
    value: usize,
    max: usize,
}

impl<'a, T> LimitTracker<'a, T>
where
    T: Messenger,
{
    pub fn new(messenger: &'a T, max: usize) -> LimitTracker<'a, T> {
        LimitTracker {
            messenger,
            value: 0,
            max,
        }
    }

    pub fn set_value(&mut self, value: usize) {
        self.value = value;

        let percentage_of_max = self.value as f64 / self.max as f64;

        if percentage_of_max >= 1.0 {
            self.messenger.send("Error: You are over your quota!");
        } else if percentage_of_max >= 0.9 {
            self.messenger
                .send("Urgent warning: You've used up over 90% of your quota!");
        } else if percentage_of_max >= 0.75 {
            self.messenger
                .send("Warning: You've used up over 75% of your quota!");
        }
    }
}
#[cfg(test)]
mod tests {
    use super::*;

    struct MockMessenger {
        sent_messages: Vec<String>,
    }

    impl MockMessenger {
        fn new() -> MockMessenger {
            MockMessenger {
                sent_messages: vec![],
            }
        }
    }

    impl Messenger for MockMessenger {
        fn send(&self, message: &str) {
            self.sent_messages.push(String::from(message));
        }
    }

    #[test]
    fn it_sends_an_over_75_percent_warning_message() {
        let mock_messenger = MockMessenger::new();
        let mut limit_tracker = LimitTracker::new(&mock_messenger, 100);

        limit_tracker.set_value(80);

        assert_eq!(mock_messenger.sent_messages.len(), 1);
    }
}
  1. Messenger ํŠธ๋ ˆ์ž‡
    • send(&self, message: &str) ๋ฉ”์„œ๋“œ๋ฅผ ๊ฐ–๊ณ  ์žˆ์Œ.
    • ์ด๋ฉ”์ผ์ด๋‚˜ ๋ฌธ์ž ์ „์†ก ๊ฐ™์€ ์—ญํ• ์„ ํ•  ์ˆ˜ ์žˆ์Œ.
    • ์‹ค์ œ ์ „์†ก์ด ์•„๋‹ˆ๋ผ, ํ…Œ์ŠคํŠธ์—์„œ๋Š” โ€˜์ „์†ก์ด ํ˜ธ์ถœ๋˜์—ˆ๋Š”์ง€โ€™๋ฅผ ํ™•์ธํ•˜๊ณ  ์‹ถ์Œ.
  2. LimitTracker
    • ์–ด๋–ค ๊ฐ’์ด max ๊ฐ’์„ ๋„˜์œผ๋ฉด, Messenger๋ฅผ ํ†ตํ•ด ๊ฒฝ๊ณ  ๋ฉ”์‹œ์ง€๋ฅผ ์ „์†กํ•จ.
    • ๊ทธ๋Ÿฐ๋ฐ set_value๋Š” ๋ฐ˜ํ™˜๊ฐ’์ด ์—†๊ธฐ ๋•Œ๋ฌธ์— ์™ธ๋ถ€์—์„œ ํ˜ธ์ถœ ๊ฒฐ๊ณผ๋ฅผ ์ง์ ‘ ํ™•์ธํ•  ์ˆ˜ ์—†์Œ.
    • ๋”ฐ๋ผ์„œ Messenger ํŠธ๋ ˆ์ž‡์„ ๊ตฌํ˜„ํ•œ ๋ชจ์˜ ๊ฐ์ฒด(mock object) ๊ฐ€ ํ•„์š”ํ•จ.
  3. MockMessenger
    • ์‹ค์ œ๋กœ ๋ฉ”์‹œ์ง€๋ฅผ ์ „์†กํ•˜์ง€ ์•Š๊ณ , Vec<String>์— ๋ฉ”์‹œ์ง€๋ฅผ ์ €์žฅ๋งŒ ํ•จ.
    • ์ด๋ ‡๊ฒŒ ์ €์žฅ๋œ ๋ฉ”์‹œ์ง€๋ฅผ ํ…Œ์ŠคํŠธ ์ฝ”๋“œ์—์„œ ๊ฒ€์‚ฌํ•ด์„œ, ๋™์ž‘์ด ์ œ๋Œ€๋กœ ๋˜์—ˆ๋Š”์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Œ.
  4. ๋ฌธ์ œ์ : ๋Ÿฌ์ŠคํŠธ์˜ borrow checker ์—๋Ÿฌ
    • send๋Š” &self๋กœ ์„ ์–ธ๋˜์–ด ์žˆ์–ด์„œ ๋ถˆ๋ณ€ ์ฐธ์กฐ์ž„.
    • ๊ทธ๋Ÿฐ๋ฐ ๋‚ด๋ถ€์—์„œ self.sent_messages.push(…)๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด์„œ ๊ฐ€๋ณ€ ์ ‘๊ทผ์„ ํ•˜๋ ค๊ณ  ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์—๋Ÿฌ ๋ฐœ์ƒ.
fn send(&self, message: &str) {
    self.sent_messages.push(String::from(message)); // &self ๋ถˆ๋ณ€ ์ฐธ์กฐ์ธ๋ฐ, ๊ฐ’์„ ๋ณ€๊ฒฝ
}
  • ์œ„์™€๊ฐ™์ด ๋ถˆ๋ณ€์ฐธ์กฐ์ธ๋ฐ ๊ฐ’์„ ๋ณ€๊ฒฝํ•˜๋ ค๊ณ  ํ•˜๋ฉด ์•ˆ๋˜๋Š” ์ƒํ™ฉ์ด ์žˆ๋‹ค.
use std::cell::RefCell;

struct MockMessenger {
    sent_messages: RefCell<Vec<String>>,
}

impl MockMessenger {
    fn new() -> MockMessenger {
        MockMessenger {
            sent_messages: RefCell::new(vec![]),
        }
    }
}

impl Messenger for MockMessenger {
    fn send(&self, message: &str) {
        self.sent_messages.borrow_mut().push(String::from(message));
    }
}
  • ์œ„์ฒ˜๋Ÿผ ํŽธํ•˜๊ฒŒ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋‹ค.

Another Useful Usecases of Refcell

lazy init, caching :

use std::cell::RefCell;

struct ExpensiveComputation {
    input: i32,
    cached_result: RefCell<Option<i32>>,
}

impl ExpensiveComputation {
    fn new(input: i32) -> Self {
        Self {
            input,
            cached_result: RefCell::new(None),
        }
    }

    fn result(&self) -> i32 {
        // ๋‚ด๋ถ€ ์ƒํƒœ๋ฅผ ๋ถˆ๋ณ€ ์ฐธ์กฐ๋กœ ๋…ธ์ถœํ•˜๋ฉด์„œ๋„ ๊ฒฐ๊ณผ๋Š” ์บ์‹ฑ ๊ฐ€๋Šฅ
        let mut cache = self.cached_result.borrow_mut();
        if let Some(value) = *cache {
            value
        } else {
            let computed = self.input * 2; // ์˜ˆ์‹œ์šฉ์œผ๋กœ ๊ฐ€๋ฒผ์šด ์—ฐ์‚ฐ, ์—ฌ๊ธฐ์— ์—„์ฒญ ํฐ ์—ฐ์‚ฐ์„ ๋‘๋Š” ๊ฑธ ์ƒ์ •ํ•œ๋‹ค
            *cache = Some(computed);
            computed
        }
    }
}

์ƒํƒœ๋ฅผ ๊ณต์œ ํ•˜๋Š” ์ผ€์ด์Šค :

use std::cell::RefCell;
use std::rc::Rc;

struct AppState {
    count: RefCell<i32>,
}

fn increment(state: &AppState) {
    *state.count.borrow_mut() += 1;
}
  • ์ฒ˜์Œ์—๋Š” ์œ„์˜ ์ฝ”๋“œ๊ฐ€ ์™€๋‹ฟ์ง€ ์•Š์„ ์ˆ˜ ์žˆ๋Š”๋ฐ, ์ด๋Ÿฐ ์ƒํƒœ๋Š” ๋ณดํ†ต ์—ฌ๋Ÿฌ ์ปดํฌ๋„ŒํŠธ๋“ค์ด ๊ณต์œ ํ•ด์•ผํ•œ๋‹ค.
  • ๊ทธ๋Ÿฌ๋ ค๋ฉด Rc๋ฅผ ์จ์•ผํ•˜๋Š”๋ฐ RefCell์œผ๋กœ ์•ˆ๋‘”๋‹ค๋ฉด,
use std::rc::Rc;

struct AppState {
    count: i32,
}

fn increment(state: &AppState) {
    state.count += 1; // โŒ ์—๋Ÿฌ: cannot assign to `state.count`, as `state` is not declared as mutable
}
  • ์œ„์™€ ๊ฐ™์ด ๋ถˆ๋ณ€ ๊ฐ์ฒด์— ๋Œ€ํ•œ ์ ‘๊ทผ์ด ๋ง‰ํžˆ๊ฒŒ ๋œ๋‹ค.
  • ๊ทธ๋ž˜์„œ ๋‹ค์‹œ ์•„๊นŒ์ฒ˜๋Ÿผ
use std::cell::RefCell;
use std::rc::Rc;

struct AppState {
    count: RefCell<i32>,
}

fn increment(state: &AppState) {
    *state.count.borrow_mut() += 1; // โœ… ๋‚ด๋ถ€ ๊ฐ€๋ณ€์„ฑ์œผ๋กœ ๊ฐ€๋Šฅ!
}
  • RecCell์„ ์ด์šฉํ•ด์„œ ํ•ด๊ฒฐ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.

15.4.4 Keeping Track of Runtime with Refcell

RefCell์€ ๋Ÿฐํƒ€์ž„์— ๋นŒ๋ฆผ ๊ทœ์น™์„ ๊ฒ€์‚ฌํ•˜๋Š” ์•ˆ์ „ํ•œ API๋ฅผ ์ œ๊ณตํ•œ๋‹ค :

  • borrow() ๋ฉ”์„œ๋“œ๋Š” Ref๋ผ๋Š” ์Šค๋งˆํŠธ ํฌ์ธํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉฐ, ์ด๋Š” ๋ถˆ๋ณ€ ์ฐธ์กฐ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค
  • borrow_mut() ๋ฉ”์„œ๋“œ๋Š” RefMut๋ผ๋Š” ์Šค๋งˆํŠธ ํฌ์ธํ„ฐ๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉฐ, ๊ฐ€๋ณ€ ์ฐธ์กฐ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค
  • ์ด ๋‘ ์Šค๋งˆํŠธ ํฌ์ธํ„ฐ๋Š” ๋ชจ๋‘ Deref ํŠธ๋ ˆ์ž‡์„ ๊ตฌํ˜„ํ•˜๋ฏ€๋กœ ์ผ๋ฐ˜ ์ฐธ์กฐ์ฒ˜๋Ÿผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค

๋Ÿฐํƒ€์ž„์— ๋ฒ„๋กœ์šฐ์ฒดํฌ์™€ painic์„ ๋ฐœ์ƒ์‹œํ‚ค๋Š” ์˜ˆ์‹œ :

impl Messenger for MockMessenger {
    fn send(&self, message: &str) {
        let mut one_borrow = self.sent_messages.borrow_mut();
        let mut two_borrow = self.sent_messages.borrow_mut();

        one_borrow.push(String::from(message));
        two_borrow.push(String::from(message));
    }
}
thread 'tests::it_sends_an_over_75_percent_warning_message' panicked at src/lib.rs:60:53:
already borrowed: BorrowMutErr

15.4.5 Having Multiple Owners of Mutable Data By Combining Rc and Refcell

์„  ์š”์•ฝ :

Rc๋งŒ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ

  • ์—ฌ๋Ÿฌ ์†Œ์œ ์ž๋ฅผ ํ—ˆ์šฉํ•˜์ง€๋งŒ, ๋ถˆ๋ณ€ ์ฐธ์กฐ๋งŒ ํ—ˆ์šฉ๋จ โ†’ ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ๋ถˆ๊ฐ€

RefCell๋งŒ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ

  • ๋‹จ์ผ ์†Œ์œ ์ž๋งŒ ๊ฐ€๋Šฅํ•˜์ง€๋งŒ, ๋‚ด๋ถ€ ๊ฐ€๋ณ€์„ฑ(Interior Mutability)์„ ํ†ตํ•ด ๋ฐ์ดํ„ฐ ๋ณ€๊ฒฝ ๊ฐ€๋Šฅ

Rc<RefCell> ์กฐํ•ฉ

  • ์—ฌ๋Ÿฌ ์†Œ์œ ์ž + ๊ฐ€๋ณ€ ์ ‘๊ทผ์„ ๋™์‹œ์— ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ์Œ
  • Rc๊ฐ€ ์†Œ์œ ๊ถŒ ๊ณต์œ ๋ฅผ, RefCell์ด ๋‚ด๋ถ€ ๊ฐ€๋ณ€์„ฑ์„ ์ œ๊ณต
#[derive(Debug)]
enum List {
    Cons(Rc<RefCell<i32>>, Rc<List>),
    Nil,
}

use crate::List::{Cons, Nil};
use std::cell::RefCell;
use std::rc::Rc;

fn main() {
    let value = Rc::new(RefCell::new(5));

    let a = Rc::new(Cons(Rc::clone(&value), Rc::new(Nil)));

    let b = Cons(Rc::new(RefCell::new(3)), Rc::clone(&a));
    let c = Cons(Rc::new(RefCell::new(4)), Rc::clone(&a));

    *value.borrow_mut() += 10;

    println!("a after = {a:?}");
    println!("b after = {b:?}");
    println!("c after = {c:?}");
}
$ cargo run
   Compiling cons-list v0.1.0 (file:///projects/cons-list)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.63s
     Running `target/debug/cons-list`
a after = Cons(RefCell { value: 15 }, Nil)
b after = Cons(RefCell { value: 3 }, Cons(RefCell { value: 15 }, Nil))
c after = Cons(RefCell { value: 4 }, Cons(RefCell { value: 15 }, Nil))

15.5 Refecence Cycles Can Leak Memory

  • ๋Ÿฌ์ŠคํŠธ์—์„œ๋„ ๋ฉ”๋ชจ๋ฆฌ leaks๋ฅผ, ์ด์žฅ์—์„œ ๋งŒ๋“  ๊ฒƒ๋“ค์„ ํ†ตํ•ด ์œ ๋ฐœํ•  ์ˆ˜ ์žˆ๋‹ค.
#[derive(Debug)]
enum List {
    Cons(i32, RefCell<Rc<List>>),
    Nil,
}

impl List {
    fn tail(&self) -> Option<&RefCell<Rc<List>>> {
        match self {
            Cons(_, item) => Some(item),
            Nil => None,
        }
    }
}
fn main() {
    let a = Rc::new(Cons(5, RefCell::new(Rc::new(Nil))));

    println!("a initial rc count = {}", Rc::strong_count(&a));
    println!("a next item = {:?}", a.tail());

    let b = Rc::new(Cons(10, RefCell::new(Rc::clone(&a))));

    println!("a rc count after b creation = {}", Rc::strong_count(&a));
    println!("b initial rc count = {}", Rc::strong_count(&b));
    println!("b next item = {:?}", b.tail());

    if let Some(link) = a.tail() {
        *link.borrow_mut() = Rc::clone(&b);
    }

    println!("b rc count after changing a = {}", Rc::strong_count(&b));
    println!("a rc count after changing a = {}", Rc::strong_count(&a));

    // Uncomment the next line to see that we have a cycle;
    // it will overflow the stack
    // println!("a next item = {:?}", a.tail());
}

์„ค๋ช…์ด ์•ฝ๊ฐ„ ๋ถ€์กฑํ•œ๋ฐ, ์—ฌ๊ธฐ์„œ ์Šคํƒ์ด ํ„ฐ์ง€๋Š” ์ด์œ ๋Š” ์ˆœํ™˜๊ตฌ์กฐ ์ž์ฒด์˜ ๋ฌธ์ œ๋Š” ์•„๋‹ˆ๊ณ  Debut Trait์˜ ์ด์Šˆ๋‹ค :

1.	a.tail() โ†’ Some(RefCell { value: Rc<List> })
2.	Debug๋Š” ์ด RefCell์„ ์ถœ๋ ฅํ•˜๋ ค๊ณ  value๋ฅผ ์ถœ๋ ฅํ•จ โ†’ ๋‚ด๋ถ€์˜ Rc<List>๋ฅผ ๋”ฐ๋ผ๊ฐ
3.	Rc<List>์˜ ๋‚ด๋ถ€๋ฅผ ์ถœ๋ ฅ โ†’ ์ด๊ฑด b
4.	๊ทธ๋Ÿฐ๋ฐ b๋„ Cons(10, RefCell<Rc<List>>) โ†’ ๋‹ค์‹œ RefCell ์•ˆ์˜ Rc<List>๋ฅผ ์ถœ๋ ฅ
5.	์ด๊ฑด ๋‹ค์‹œ a โ†’ ๋ฌดํ•œ ๋ฐ˜๋ณตโ€ฆ

๊ทธ๋ฆฌ๊ณ  drop์ด ํ˜ธ์ถœ๋˜์—ˆ์„๋•Œ๋„ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค : leakDrop

  • ์ถœ์ฒ˜ : https://doc.rust-lang.org/book/ch15-06-reference-cycles.html#reference-cycles-can-leak-memory
  • ์ฐธ์กฐ์นด์šดํŠธ๋Š” ๋ณ€์ˆ˜ํ• ๋‹น ์œผ๋กœ ๊ฐ๊ฐ 1๋ฒˆ, RcList์˜ ์ฐธ์กฐ๊ฐ€ ์ผ์–ด๋‚˜๋ฉด์„œ ๊ฐ๊ฐ ํ•œ๋ฒˆ์”ฉ ์ƒ์„ฑ๋œ๋‹ค.
    • a.tail()์— b๋ฅผ ํ• ๋‹นํ•˜๋ฉด์„œ ์ƒ๊ธด ์นด์šดํŠธ ํฌํ•จ
  • drop์œผ๋กœ a, b๋ฅผ ์ •๋ฆฌํ•ด๋„ tail, rcList์ฐธ์กฐ๋กœ ์ƒ๊ธด ์ฐธ์กฐ์นด์šดํŠธ๋Š” ์ค„์–ด๋“ค์ง€ ์•Š๋Š”๋‹ค.
  • ๊ฒฐ๊ตญ ๋ฉ”๋ชจ๋ฆฌ ์œ ์ถœ์ด ๋ฐœ์ƒํ•œ๋‹ค.

15.5.1 Preventing Reference Cycles: Turning an Rc into a Weak

์ง€๊ธˆ๊นŒ์ง€ ์šฐ๋ฆฌ๋Š” Rc::clone์„ ํ˜ธ์ถœํ•˜๋ฉด Rc ์ธ์Šคํ„ด์Šค์˜ strong_count๊ฐ€ ์ฆ๊ฐ€ํ•˜๋ฉฐ, Rc ์ธ์Šคํ„ด์Šค๋Š” strong_count๊ฐ€ 0์ผ ๋•Œ๋งŒ ์ •๋ฆฌ๋œ๋‹ค๋Š” ๊ฒƒ์„ ๋ณด์—ฌ์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค. Rc ์ธ์Šคํ„ด์Šค ๋‚ด์˜ ๊ฐ’์— ๋Œ€ํ•œ *์•ฝํ•œ ์ฐธ์กฐ(weak reference)*๋„ Rc::downgrade๋ฅผ ํ˜ธ์ถœํ•˜๊ณ  Rc์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ์ „๋‹ฌํ•˜์—ฌ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฐ•ํ•œ ์ฐธ์กฐ๋Š” Rc ์ธ์Šคํ„ด์Šค์˜ ์†Œ์œ ๊ถŒ์„ ๊ณต์œ ํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ์•ฝํ•œ ์ฐธ์กฐ๋Š” ์†Œ์œ ๊ถŒ ๊ด€๊ณ„๋ฅผ ํ‘œํ˜„ํ•˜์ง€ ์•Š์œผ๋ฉฐ, ๊ทธ ๊ฐœ์ˆ˜๋Š” Rc ์ธ์Šคํ„ด์Šค๊ฐ€ ์ •๋ฆฌ๋˜๋Š” ์‹œ์ ์— ์˜ํ–ฅ์„ ๋ฏธ์น˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด๋“ค์€ ์ฐธ์กฐ ์ˆœํ™˜์„ ์ผ์œผํ‚ค์ง€ ์•Š์„ ๊ฒƒ์ธ๋ฐ, ์•ฝํ•œ ์ฐธ์กฐ๋ฅผ ํฌํ•จํ•˜๋Š” ๋ชจ๋“  ์ˆœํ™˜์€ ๊ด€๋ จ๋œ ๊ฐ’๋“ค์˜ ๊ฐ•ํ•œ ์ฐธ์กฐ ๊ฐœ์ˆ˜๊ฐ€ 0์ด ๋˜๋ฉด ๋Š์–ด์งˆ ๊ฒƒ์ด๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

Rc::downgrade๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด Weak ํƒ€์ž…์˜ ์Šค๋งˆํŠธ ํฌ์ธํ„ฐ๋ฅผ ์–ป๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. Rc ์ธ์Šคํ„ด์Šค์˜ strong_count๋ฅผ 1 ์ฆ๊ฐ€์‹œํ‚ค๋Š” ๋Œ€์‹ , Rc::downgrade๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด weak_count๊ฐ€ 1 ์ฆ๊ฐ€ํ•ฉ๋‹ˆ๋‹ค. Rc ํƒ€์ž…์€ strong_count์™€ ์œ ์‚ฌํ•˜๊ฒŒ weak_count๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์–ผ๋งˆ๋‚˜ ๋งŽ์€ Weak ์ฐธ์กฐ๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€ ์ถ”์ ํ•ฉ๋‹ˆ๋‹ค. ์ฐจ์ด์ ์€ Rc ์ธ์Šคํ„ด์Šค๊ฐ€ ์ •๋ฆฌ๋˜๊ธฐ ์œ„ํ•ด weak_count๊ฐ€ 0์ผ ํ•„์š”๋Š” ์—†๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

Weak๊ฐ€ ์ฐธ์กฐํ•˜๋Š” ๊ฐ’์ด ์ด๋ฏธ ์‚ญ์ œ๋˜์—ˆ์„ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, Weak๊ฐ€ ๊ฐ€๋ฆฌํ‚ค๋Š” ๊ฐ’์œผ๋กœ ๋ฌด์–ธ๊ฐ€๋ฅผ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ๊ทธ ๊ฐ’์ด ์—ฌ์ „ํžˆ ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” Weak ์ธ์Šคํ„ด์Šค์˜ upgrade ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ ์ˆ˜ํ–‰ํ•˜๋Š”๋ฐ, ์ด ๋ฉ”์„œ๋“œ๋Š” OptionRc๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. Rc ๊ฐ’์ด ์•„์ง ์‚ญ์ œ๋˜์ง€ ์•Š์•˜๋‹ค๋ฉด Some ๊ฒฐ๊ณผ๋ฅผ ์–ป๊ณ , Rc ๊ฐ’์ด ์‚ญ์ œ๋˜์—ˆ๋‹ค๋ฉด None ๊ฒฐ๊ณผ๋ฅผ ์–ป์Šต๋‹ˆ๋‹ค. upgrade๋Š” OptionRc๋ฅผ ๋ฐ˜ํ™˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, Rust๋Š” Some ๊ฒฝ์šฐ์™€ None ๊ฒฝ์šฐ๊ฐ€ ์ฒ˜๋ฆฌ๋˜๋„๋ก ๋ณด์žฅํ•˜๋ฉฐ, ์œ ํšจํ•˜์ง€ ์•Š์€ ํฌ์ธํ„ฐ๋Š” ์—†์„ ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋ฌธ์ œ์˜ ์ถœ๋ฐœ์ : Rc๋Š” ์ˆœํ™˜ ์ฐธ์กฐ๋ฅผ ์œ ๋ฐœํ•  ์ˆ˜ ์žˆ์Œ

  • Rc::clone์€ Rc์˜ strong_count๋ฅผ ์ฆ๊ฐ€์‹œํ‚ด.
  • Rc๋Š” strong_count๊ฐ€ 0์ผ ๋•Œ๋งŒ ํž™์—์„œ drop๋จ.
  • ๋”ฐ๋ผ์„œ ๋‘ ๊ฐœ์ฒด๊ฐ€ ์„œ๋กœ๋ฅผ Rc๋กœ ์ฐธ์กฐํ•˜๋ฉด reference cycle์ด ๋ฐœ์ƒํ•˜๊ณ , ์ด๋Š” ๋ฉ”๋ชจ๋ฆฌ ํ•ด์ œ๊ฐ€ ๋ถˆ๊ฐ€๋Šฅํ•ด์ง.

ํ•ด๊ฒฐ์ฑ…: Rc::downgrade๋ฅผ ํ†ตํ•ด Weak์ƒ์„ฑ

  • Rc::downgrade(&rc_value) ํ˜ธ์ถœ ์‹œ Weak ์Šค๋งˆํŠธ ํฌ์ธํ„ฐ ์ƒ์„ฑ.
  • ์ด ํฌ์ธํ„ฐ๋Š” ์†Œ์œ ๊ถŒ์„ ๊ฐ–์ง€ ์•Š์Œ (strong_count์— ์˜ํ–ฅ ์—†์Œ).
  • ๋Œ€์‹  weak_count๋ฅผ 1 ์ฆ๊ฐ€์‹œํ‚ด.
  • Weak๋Š” ํž™ ํ•ด์ œ์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ์ฐธ์กฐ ์‚ฌ์ดํด์„ ๋ฐฉ์ง€.

Weak๊ฐ’ ์ ‘๊ทผ: .upgrade() ๋ฉ”์„œ๋“œ

  • Weak๋Š” ํ•ด๋‹น ๊ฐ’์ด ์‚ด์•„ ์žˆ์„ ์ˆ˜๋„ ์žˆ๊ณ  ์•„๋‹ ์ˆ˜๋„ ์žˆ์Œ.
  • upgrade() ํ˜ธ์ถœ ์‹œ Option<Rc<» ๋ฐ˜ํ™˜.
    • ๊ฐ’์ด ์‚ด์•„ ์žˆ์œผ๋ฉด Some(Rc<>)
    • drop๋˜์—ˆ์œผ๋ฉด None
use std::cell::RefCell;
use std::rc::Rc;

#[derive(Debug)]
struct Node {
    value: i32,
    children: RefCell<Vec<Rc<Node>>>,
}

Node๋ผ๋Š” ์ด๋ฆ„์˜ ๊ตฌ์กฐ์ฒด๋ฅผ ๋งŒ๋“ค๊ณ , ๊ทธ ์•ˆ์— ์ž์‹ ์˜ i32 ๊ฐ’๊ณผ ์ž์‹ Node ๊ฐ’๋“ค์— ๋Œ€ํ•œ ์ฐธ์กฐ๋ฅผ ๋ณด๊ด€:

  • Node๊ฐ€ ์ž์‹ ๋…ธ๋“œ๋“ค์„ ์†Œ์œ ํ•˜๊ธธ ์›ํ•˜๊ณ , ๊ฐ ๋…ธ๋“œ์— ์ง์ ‘ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ์—ฌ๋Ÿฌ ๋ณ€์ˆ˜๋“ค๊ณผ ๊ทธ ์†Œ์œ ๊ถŒ์„ ๊ณต์œ ํ•˜๊ธธ ์›ํ•จ.
  • ์ด๋ฅผ ์œ„ํ•ด Vec<> ํ•ญ๋ชฉ์˜ ํƒ€์ž…์„ RcNode๋กœ ์ •์˜
  • ๋˜ํ•œ ์–ด๋–ค ๋…ธ๋“œ๊ฐ€ ๋‹ค๋ฅธ ๋…ธ๋“œ์˜ ์ž์‹์ธ์ง€ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๊ธธ ์›ํ•˜๋ฏ€๋กœ, children ํ•„๋“œ๋Š” Vec<Rc<»๋ฅผ ๊ฐ์‹ผ RefCell<>๋กœ ์ •์˜
fn main() {
    let leaf = Rc::new(Node {
        value: 3,
        children: RefCell::new(vec![]),
    });

    let branch = Rc::new(Node {
        value: 5,
        children: RefCell::new(vec![Rc::clone(&leaf)]),
    });
}

leaf์˜ RcNode๋ฅผ ๋ณต์ œํ•˜์—ฌ branch์— ์ €์žฅ, ์ด๋Š” leaf์— ์žˆ๋Š” ๋…ธ๋“œ๊ฐ€ ๋‘ ๋ช…์˜ ์†Œ์œ ์ž(leaf์™€ branch)๋ฅผ ๊ฐ–๊ฒŒ ๋œ๋‹ค๋Š” ์˜๋ฏธ

  • ์ด๋Ÿฌ๋ฉด branch์—์„œ leaf๋ฅผ ๊ฐˆ์ˆ˜๋Š” ์žˆ๋Š”๋ฐ, ๋ฐ˜๋Œ€๋Š” ์•ˆ๋จ
use std::cell::RefCell;
use std::rc::{Rc, Weak};

#[derive(Debug)]
struct Node {
    value: i32,
    parent: RefCell<Weak<Node>>,
    children: RefCell<Vec<Rc<Node>>>,
}
  • Node ๊ตฌ์กฐ์ฒด ์ •์˜์— parent ํ•„๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด์•ผ ํ•œ๋‹ค.
  • ์—ฌ๊ธฐ์„œ ๋ฌธ์ œ๋Š” parent์˜ ํƒ€์ž…์„ ๋ฌด์—‡์œผ๋กœ ํ•ด์•ผ ํ•˜๋А๋ƒ์ธ๋ฐ,
  • Rc<>๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์•ˆ ๋จ.
  • ์™œ๋ƒํ•˜๋ฉด leaf.parent๊ฐ€ branch๋ฅผ ๊ฐ€๋ฆฌํ‚ค๊ณ  branch.children์ด ๋‹ค์‹œ leaf๋ฅผ ๊ฐ€๋ฆฌํ‚ค๋Š” reference cycle์„ ๋งŒ๋“ค๊ฒŒ ๋˜๊ธฐ ๋•Œ๋ฌธ

๊ด€๊ณ„๋ฅผ ๋‹ค์‹œ ์ƒ๊ฐํ•ด๋ณด๋ฉด, ๋ถ€๋ชจ ๋…ธ๋“œ๋Š” ์ž์‹ ๋…ธ๋“œ๋ฅผ ์†Œ์œ ํ•ด์•ผ ํ•จ, ๋งŒ์•ฝ ๋ถ€๋ชจ ๋…ธ๋“œ๊ฐ€ ์ œ๊ฑฐ๋˜๋ฉด, ์ž์‹ ๋…ธ๋“œ๋“ค๋„ ์ œ๊ฑฐ๋˜์–ด์•ผ ํ•˜์ง€๋งŒ. ์ž์‹ ๋…ธ๋“œ๋Š” ๋ถ€๋ชจ ๋…ธ๋“œ๋ฅผ ์†Œ์œ ํ•ด์„œ๋Š” ์•ˆ ๋จ.

์ž์‹ ๋…ธ๋“œ๊ฐ€ ์ œ๊ฑฐ๋˜์–ด๋„ ๋ถ€๋ชจ๋Š” ๊ณ„์† ์กด์žฌํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ. ์ด ๊ฒฝ์šฐ๋Š” ์•ฝํ•œ ์ฐธ์กฐ(weak references) ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•œ๋‹ค.

fn main() {
    let leaf = Rc::new(Node {
        value: 3,
        parent: RefCell::new(Weak::new()),
        children: RefCell::new(vec![]),
    });

    println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());

    let branch = Rc::new(Node {
        value: 5,
        parent: RefCell::new(Weak::new()),
        children: RefCell::new(vec![Rc::clone(&leaf)]),
    });

    *leaf.parent.borrow_mut() = Rc::downgrade(&branch);

    println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
}
fn main() {
    let leaf = Rc::new(Node {
        value: 3,
        parent: RefCell::new(Weak::new()),
        children: RefCell::new(vec![]),
    });

    println!(
        "leaf strong = {}, weak = {}",
        Rc::strong_count(&leaf),
        Rc::weak_count(&leaf),
    ); // leaf strong = 1, weak = 0

    {
        let branch = Rc::new(Node {
            value: 5,
            parent: RefCell::new(Weak::new()),
            children: RefCell::new(vec![Rc::clone(&leaf)]),
        });

        *leaf.parent.borrow_mut() = Rc::downgrade(&branch);

        println!(
            "branch strong = {}, weak = {}",
            Rc::strong_count(&branch),
            Rc::weak_count(&branch),
        ); // branch strong = 1, weak = 1

        println!(
            "leaf strong = {}, weak = {}",
            Rc::strong_count(&leaf),
            Rc::weak_count(&leaf),
        ); // leaf strong = 2, weak = 0
    } // ์—ฌ๊ธฐ์„œ branch๋Š” ์Šค์ฝ”ํ”„๋ฅผ ๋ฒ—์–ด๋‚˜๋ฏ€๋กœ drop๋จ

    println!("leaf parent = {:?}", leaf.parent.borrow().upgrade());
    // leaf parent = None

    println!(
        "leaf strong = {}, weak = {}",
        Rc::strong_count(&leaf),
        Rc::weak_count(&leaf),
    ); // leaf strong = 1, weak = 0
}