라이프타임은 Rust의 가장 독특한 특징입니다. 참조가 유효한 스코프를 표현하며, 컴파일러가 댕글링 참조를 방지할 수 있게 합니다.
모든 참조는 라이프타임을 가집니다. 대부분의 경우 컴파일러가 추론하지만, 명시적으로 지정해야 할 때도 있습니다.
// 가장 긴 문자열을 반환하는 함수
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 사용하면 컴파일 에러!
}
컴파일러는 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[..]
}
구조체가 참조를 포함할 때는 명시적 라이프타임이 필요합니다.
// 텍스트의 일부를 참조하는 구조체
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);
}
// 프로그램 전체 수명을 가지는 참조
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())
}
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);
}
}
// 트레이트와 라이프타임 결합
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
}
}
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));
}
}
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
}
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);
// 순환 참조 없이 양방향 관계 구현!
}
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);
}
}