1장: 소유권 심화 & 라이프타임

1.1 라이프타임의 본질

라이프타임은 Rust의 가장 독특한 특징입니다. 참조가 유효한 스코프를 표현하며, 컴파일러가 댕글링 참조를 방지할 수 있게 합니다.

핵심 원칙:

모든 참조는 라이프타임을 가집니다. 대부분의 경우 컴파일러가 추론하지만, 명시적으로 지정해야 할 때도 있습니다.

1.1.1 명시적 라이프타임 표기

// 가장 긴 문자열을 반환하는 함수
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

fn main() {
    let string1 = String::from("long string is long");
    let result;
    {
        let string2 = String::from("xyz");
        result = longest(string1.as_str(), string2.as_str());
        println!("The longest string is {}", result);
    }
    // 여기서 result 사용하면 컴파일 에러!
}

1.1.2 라이프타임 엘리전 규칙

컴파일러는 3가지 규칙으로 라이프타임을 자동 추론합니다:

// 규칙 2가 적용되는 경우
fn first_word(s: &str) -> &str {
    // 컴파일러가 다음처럼 추론:
    // fn first_word<'a>(s: &'a str) -> &'a str
    let bytes = s.as_bytes();
    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];
        }
    }
    &s[..]
}

1.2 구조체와 라이프타임

구조체가 참조를 포함할 때는 명시적 라이프타임이 필요합니다.

// 텍스트의 일부를 참조하는 구조체
struct Excerpt<'a> {
    part: &'a str,
}

impl<'a> Excerpt<'a> {
    fn level(&self) -> i32 {
        3
    }

    fn announce_and_return_part(&self, announcement: &str) -> &str {
        println!("Attention please: {}", announcement);
        self.part
    }
}

fn main() {
    let novel = String::from("Call me Ishmael. Some years ago...");
    let first_sentence = novel.split('.').next().expect("Could not find a '.'");
    let i = Excerpt {
        part: first_sentence,
    };
    println!("Excerpt: {}", i.part);
}

1.3 고급 라이프타임 패턴

1.3.1 정적 라이프타임 ('static)

// 프로그램 전체 수명을 가지는 참조
let s: &'static str = "I have a static lifetime.";

// 정적 변수
static GLOBAL: &str = "Global static string";

// Leak을 사용한 동적 정적 참조
fn leak_string() -> &'static str {
    let s = String::from("leaked");
    Box::leak(s.into_boxed_str())
}
⚠️ 주의: 'static을 남용하지 마세요. 대부분의 경우 필요하지 않으며, 메모리 누수의 원인이 될 수 있습니다.

1.3.2 다중 라이프타임 매개변수

struct Context<'s, 'c> {
    search: &'s str,
    content: &'c str,
}

impl<'s, 'c> Context<'s, 'c> {
    fn find(&self) -> Option<&'c str> {
        self.content.find(self.search)
            .map(|i| &self.content[i..])
    }
}

fn main() {
    let search_term = "Rust";
    let content = String::from("Rust is awesome!");

    let ctx = Context {
        search: search_term,
        content: &content,
    };

    if let Some(result) = ctx.find() {
        println!("Found: {}", result);
    }
}

1.3.3 라이프타임 바운드

// 트레이트와 라이프타임 결합
use std::fmt::Display;

fn longest_with_announcement<'a, T>(
    x: &'a str,
    y: &'a str,
    ann: T,
) -> &'a str
where
    T: Display,
{
    println!("Announcement! {}", ann);
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

1.4 고급 소유권 패턴

1.4.1 내부 가변성 (Interior Mutability)

use std::cell::RefCell;

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

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

    fn send(&self, message: &str) {
        // 불변 참조로 가변 데이터 수정!
        self.sent_messages.borrow_mut().push(String::from(message));
    }
}

1.4.2 참조 카운팅 (Rc와 Arc)

use std::rc::Rc;

struct Node {
    value: i32,
    children: Vec<Rc<Node>>,
}

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

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

    println!("leaf strong count: {}", Rc::strong_count(&leaf)); // 2
}

1.4.3 순환 참조 방지 (Weak)

use std::rc::{Rc, Weak};
use std::cell::RefCell;

struct Node {
    value: i32,
    parent: RefCell<Weak<Node>>,
    children: RefCell<Vec<Rc<Node>>>,
}

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

    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);

    // 순환 참조 없이 양방향 관계 구현!
}

1.5 실전 예제: 텍스트 파서

struct Parser<'a> {
    content: &'a str,
    pos: usize,
}

impl<'a> Parser<'a> {
    fn new(content: &'a str) -> Self {
        Parser { content, pos: 0 }
    }

    fn parse_word(&mut self) -> Option<&'a str> {
        let start = self.pos;

        while self.pos < self.content.len() {
            if self.content.as_bytes()[self.pos].is_ascii_whitespace() {
                break;
            }
            self.pos += 1;
        }

        if start == self.pos {
            None
        } else {
            let word = &self.content[start..self.pos];
            self.pos += 1; // 공백 건너뛰기
            Some(word)
        }
    }
}

fn main() {
    let text = "Hello Rust World";
    let mut parser = Parser::new(text);

    while let Some(word) = parser.parse_word() {
        println!("Word: {}", word);
    }
}
🎯 핵심 요약: