&str와 lifetime

&str는 string literal과 빌림 문자열 두 가지가 있습니다. string literal은 String이 아니고, 큰 따옴표안에 들어가 있는 문자열이고, 빌림 문자열은 문자열을 빌리는 것입니다.

string literal은 static 수명이 있고, 빌림 문자열은 필요한 경우에 static이 아닌 수명을 지정해줘야 합니다.

1. 예시

가. string literal 예시

fn main() {
    let my_str = "I am a string";    
    println!("{}", my_str);
}

my_str 다음의 숨겨진 타입을 보면 &’static str로 되어 있습니다.

my_str의 타입이 &'static str임

println!(“{}”, my_str);을 println!(“{}”, &my_str);로 my_str 앞에 &를 붙여도 동작합니다.

나. &str 예시

fn print_str(my_str: &str) {
    println!("{my_str}");
}

fn main() {
    let my_str = "I am a string".to_string();
    print_str(&my_str);
}

my_str은 .to_string() 함수를 적용해서 string literal을 String 타입으로 변환했고, 이것을 빌리기 위해 my_str앞에 &를 붙여서 print_str의 인수로 전달했습니다.

2. 문자열 리터럴의 lifetime

fn returns_str() -> &str {
    "I am a str"
}

fn main() {
    let my_str = returns_str();
    println!("{my_str}");
}

위 코드는 returns_str 함수를 이용해 문자열을 리턴 받은 후 그 문자열을 화면에 출력하려고 하는 것인데,

명명된 lifetime parameter가 필요함

리턴 타입에서 명명된 라이프타임 파라미터가 기대된다는 에러 메시지가 나오면서 중간에 녹색으로 & 다음에 ‘static을 붙이는 것을 고려하라고 합니다.

반환 값이 “I am a str”, 다시 말해 문자열 리터럴이므로 &’static을 붙여야 합니다.

수정된 코드는 아래와 같습니다.

fn returns_str() -> &'static str {
    "I am a str"
}

fn main() {
    let my_str = returns_str();
    println!("{my_str}");
}

코드를 수정하고 실행하니 ‘I am a str’가 잘 출력됩니다.

'static을 추가해서 화면 출력이 잘 됨

위에 &str을 String으로 바꿔보라고 하는 제안도 있었는데, 이것을 적용하면 아래와 같이 됩니다.

fn returns_str() -> String {
    "I am a str".to_string()
}

fn main() {
    let my_str = returns_str();
    println!("{my_str}");
}

String 타입으로 바꾸면 수명 문제도 발생하지 않고, 출력값도 같습니다.

3. &str의 lifetime 1

#[derive(Debug)]
struct City {
    name: &str,
    date_founded: u32,
}

fn main() {
    let city_names = vec![String::from("Hoccimin"), String::from("Busan")];
    let my_city = City {
        name: &city_names[0],
        date_founded: 1952,
    };

    println!("{my_city:?}");
}

위 코드는 City 구조체를 만든 후 my_city라는 인스턴스를 만드는데, name으로 city_names라는 Vector에서 첫번째 것을 빌림으로 가져와서 대입하고, date_founded는 직접 1952를 입력한 다음 화면에 출력하는 것입니다.

그런데, 실행하면

빌림 문자열인 경우의 lifetime parameter 문제

&str에 명명된 lifetime parameter가 기대된다고 하면서

struct City 다음에 <‘a>를 붙이고, name 필드의 & 다음에도 ‘a를 붙이라고 합니다. 이렇게 하면 “name이 City 만큼 오래 살아야” 하므로 문제가 해결됩니다.

여기서 a는 라이프 타임 파라미터를 지칭하는 것으로 다른 문자를 사용해도 됩니다.

아래와 같이 수정하고 실행하면

#[derive(Debug)]
struct City<'a> {
    name: &'a str,
    date_founded: u32,
}

fn main() {
    let city_names = vec![String::from("Hoccimin"), String::from("Busan")];
    let my_city = City {
        name: &city_names[0],
        date_founded: 1952,
    };

    println!("{my_city:?}");
}

잘 출력됩니다.

lifetime parameter인 'a를 추가해서 화면 출력 성공

4. &str의 lifetime 2

그러나, &city_names[0]이 문자열 리터럴이 아니고 빌림 문자열이기 때문에 City 필드를 name: &’static str로 수정하면

#[derive(Debug)]
struct City {
    name: &'static str,
    date_founded: u32,
}

fn main() {
    let city_names = vec![String::from("Hoccimin"), String::from("Busan")];
    let my_city = City {
        name: &city_names[0],
        date_founded: 1952,
    };

    println!("{my_city:?}");
}

아래와 같은 에러가 발생합니다.

'a가 아닌 'static을 추가해서 에러가 발생

“&city_names[0]이라는 빌린 값이 충분히 오래 살지 못한다”고 하고,
그 아래에서는 “이 사용법은 city_names가 ‘static을 위해 빌리는 것이 요구된다”고 합니다. 왜냐하면 City 구조체 정의 시 name 타입 지정시’static 을 사용했기 때문입니다.

따라서, 3번에서와 같이 ‘a를 사용해야 합니다.

그런데 City 오른쪽에 <‘a>를 추가하지 않고, name 오른쪽에 만 ‘a를 추가하면

struct City {
name: &'a str,
date_founded: u32,
}
lifetime을 선언하지 않고 사용해 에러 발생

선언되지 않은 라이프타임 에러가 발생합니다. 따라서, City 오른쪽에 <‘a>를 추가해서 lifetime을 선언해야 합니다.

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다