🦀 Rust Learning Ebook
EN KO

🦀 Chapter 4: 빌림과 라이프타임

빌림(Borrowing)

"소유권을 이전하지 않고 값을 사용하는 Rust의 우아한 메커니즘"

소유권 시스템이 Rust의 심장이라면, 빌림은 Rust의 혈관입니다. 빌림을 마스터하면 유연하고 효율적인 코드를 작성할 수 있습니다.

4.1 참조(References)의 개념

3장에서 배운 소유권 시스템은 강력하지만, 매번 소유권을 이동시키는 것은 불편합니다. 함수에 값을 전달할 때마다 소유권을 잃고, 다시 받기 위해 반환값으로 돌려받아야 합니다.

참조(Reference)는 이 문제를 해결합니다. 참조를 사용하면 소유권을 이전하지 않고 값을 "빌려서" 사용할 수 있습니다.

4.1.1 기본 참조 사용법

fn main() {
    let s1 = String::from("hello");
    let len = calculate_length(&s1);  // &s1: s1의 참조를 전달

    println!("The length of '{}' is {}.", s1, len);  // s1 여전히 유효!
}

fn calculate_length(s: &String) -> usize {  // &String: String의 참조를 받음
    s.len()
}  // s가 스코프를 벗어나지만, 소유권이 없으므로 아무것도 해제되지 않음

&s1 구문은 s1의 참조를 생성합니다. 참조는 값을 가리키지만 소유하지 않습니다. 따라서 참조가 스코프를 벗어나도 원본 값은 drop되지 않습니다.

4.1.2 참조와 포인터의 차이

C/C++의 포인터와 달리, Rust의 참조는 항상 유효함이 보장됩니다. 널(null) 참조는 존재하지 않으며, 댕글링 참조(dangling reference)도 컴파일 타임에 방지됩니다.

항목 C/C++ 포인터 Rust 참조
Null 가능 예 (nullptr, NULL) 아니오 (Option<&T> 사용)
댕글링 가능 예 (런타임 버그) 아니오 (컴파일 에러)
포인터 연산 가능 (위험함) 불가능 (안전함)
자동 역참조 불가능 가능 (. 연산자 사용 시)

4.2 불변 참조와 가변 참조

Rust는 두 종류의 참조를 제공합니다: 불변 참조(&T)가변 참조(&mut T)입니다.

4.2.1 불변 참조 (&T)

불변 참조 (&T)

데이터를 읽을 수 있지만 수정할 수 없습니다. "읽기 전용 접근"으로 생각할 수 있습니다.

fn main() {
    let s = String::from("hello");
    let r1 = &s;
    let r2 = &s;  // 여러 개의 불변 참조 가능

    println!("{} and {}", r1, r2);
    // s.push_str(", world");  // 에러! s가 불변 참조로 빌려졌으므로 수정 불가
}

4.2.2 가변 참조 (&mut T)

가변 참조 (&mut T)

데이터를 읽고 수정할 수 있습니다. 하지만 중요한 제약이 있습니다.

fn main() {
    let mut s = String::from("hello");  // mut 키워드 필수
    change(&mut s);
    println!("{}", s);  // "hello, world"
}

fn change(some_string: &mut String) {
    some_string.push_str(", world");
}

4.2.3 가변 변수 vs 가변 참조

// 가변 변수 (mut)
let mut x = 5;
x = 6;  // OK: x 자체를 수정

// 가변 참조 (&mut)
let mut s = String::from("hello");
let r = &mut s;
r.push_str(", world");  // OK: r을 통해 s를 수정

4.3 빌림 규칙 (Borrowing Rules)

Rust의 빌림 시스템은 두 가지 핵심 규칙으로 데이터 경쟁(data race)을 컴파일 타임에 방지합니다.

규칙 1: 하나의 가변 참조 또는 여러 개의 불변 참조

특정 스코프 내에서, 어떤 값에 대해 다음 중 하나만 가질 수 있습니다:

  • 하나의 가변 참조 (&mut T), 또는
  • 여러 개의 불변 참조 (&T)

하지만 가변 참조와 불변 참조를 동시에 가질 수 없습니다!

4.3.1 규칙 1의 예제

// OK: 여러 개의 불변 참조
let s = String::from("hello");
let r1 = &s;
let r2 = &s;
let r3 = &s;
println!("{}, {}, {}", r1, r2, r3);  // 모두 사용 가능

// OK: 하나의 가변 참조
let mut s = String::from("hello");
let r1 = &mut s;
r1.push_str(", world");

// 에러: 가변 참조와 불변 참조를 동시에
let mut s = String::from("hello");
let r1 = &s;
let r2 = &mut s;  // 컴파일 에러!
println!("{}", r1);  // r1이 여기서 사용되므로 에러

// OK: 참조의 스코프가 겹치지 않음
let mut s = String::from("hello");
let r1 = &s;
let r2 = &s;
println!("{} and {}", r1, r2);
// r1과 r2는 여기서 마지막으로 사용됨

let r3 = &mut s;  // OK: r1, r2의 스코프가 끝났음
r3.push_str(", world");
println!("{}", r3);

4.3.2 왜 이런 규칙이 필요한가?

이 규칙은 데이터 경쟁(data race)을 방지합니다. 데이터 경쟁은 다음 세 가지 조건이 동시에 발생할 때 일어납니다:

데이터 경쟁 발생 조건:

  1. 둘 이상의 포인터가 같은 데이터에 동시에 접근
  2. 적어도 하나의 포인터가 데이터를 쓰는 데 사용됨
  3. 데이터 접근을 동기화하는 메커니즘이 없음

Rust의 빌림 규칙은 조건 1과 2를 컴파일 타임에 차단합니다!

규칙 2: 참조는 항상 유효해야 한다

참조는 그것이 가리키는 데이터보다 오래 살 수 없습니다. 댕글링 참조(dangling reference)가 발생하지 않도록 컴파일러가 보장합니다.

4.3.3 댕글링 참조 방지

// 컴파일 에러: 댕글링 참조
fn dangle() -> &String {
    let s = String::from("hello");
    &s  // 에러! s는 함수가 끝나면 drop되는데, 그 참조를 반환하려고 함
}  // s가 여기서 drop됨

// 해결책 1: 소유권을 반환
fn no_dangle() -> String {
    let s = String::from("hello");
    s  // OK: 소유권이 호출자에게 이동
}

// 해결책 2: 정적 수명 사용
fn static_str() -> &'static str {
    "hello"  // OK: 문자열 리터럴은 프로그램 전체 수명을 가짐
}

4.4 라이프타임(Lifetimes)

라이프타임은 참조가 유효한 범위를 나타냅니다. 대부분의 경우 Rust 컴파일러가 자동으로 추론하지만, 때로는 명시적으로 표시해야 합니다.

4.4.1 라이프타임 문법

라이프타임은 'a, 'b 같은 작은따옴표로 시작하는 이름으로 표시됩니다. 일반적으로 'a (라이프타임 a)를 가장 많이 사용합니다.

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

이 함수 시그니처는 다음을 의미합니다:

4.4.2 라이프타임 추론 규칙

Rust 컴파일러는 세 가지 규칙으로 라이프타임을 자동 추론합니다. 이 규칙으로 추론되지 않는 경우에만 명시적 표기가 필요합니다.

규칙 설명
규칙 1 각 참조 매개변수는 자신만의 라이프타임을 가진다
fn foo(x: &i32, y: &i32)fn foo<'a, 'b>(x: &'a i32, y: &'b i32)
규칙 2 입력 라이프타임이 정확히 하나면, 모든 출력 라이프타임에 적용
fn foo<'a>(x: &'a i32) -> &'a i32
규칙 3 메서드에서 여러 입력 라이프타임이 있고 그 중 하나가 &self나 &mut self면, self의 라이프타임이 모든 출력 라이프타임에 적용

4.4.3 실전 라이프타임 예제

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);  // OK
    }

    // println!("The longest string is {}", result);  // 에러!
    // string2의 라이프타임이 끝났으므로 result도 무효
}

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() {
        x
    } else {
        y
    }
}

4.5 구조체의 라이프타임

구조체가 참조를 포함하는 경우, 라이프타임 표기가 필요합니다.

struct ImportantExcerpt<'a> {
    part: &'a str,  // 'a: 이 참조는 구조체와 같은 라이프타임을 가져야 함
}

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 = ImportantExcerpt {
        part: first_sentence,
    };

    println!("Excerpt: {}", i.part);
}  // novel이 여기서 drop되기 전에 i도 drop되어야 함 (라이프타임 보장)

4.6 특수 라이프타임: 'static

'static 라이프타임은 프로그램의 전체 수명 동안 유효합니다. 모든 문자열 리터럴은 'static 라이프타임을 가집니다.

let s: &'static str = "I have a static lifetime.";

// 문자열 리터럴은 바이너리에 직접 저장되므로 항상 유효함

주의: 'static을 남용하지 마세요. 대부분의 경우 라이프타임 문제는 댕글링 참조나 잘못된 라이프타임 명시 때문입니다. 'static은 진짜로 프로그램 전체 수명 동안 유효한 데이터에만 사용해야 합니다.


4.7 빌림의 실전 패턴

4.7.1 패턴 1: 반복자(Iterator)와 빌림

fn main() {
    let v = vec![1, 2, 3, 4, 5];

    // iter(): 불변 참조 반복
    for val in v.iter() {
        println!("{}", val);  // val은 &i32 타입
    }
    println!("v still valid: {:?}", v);  // v 여전히 유효

    // iter_mut(): 가변 참조 반복
    let mut v2 = vec![1, 2, 3, 4, 5];
    for val in v2.iter_mut() {
        *val += 10;  // val은 &mut i32 타입
    }
    println!("v2: {:?}", v2);  // [11, 12, 13, 14, 15]

    // into_iter(): 소유권 이동
    let v3 = vec![1, 2, 3, 4, 5];
    for val in v3.into_iter() {
        println!("{}", val);  // val은 i32 타입 (소유권 이동)
    }
    // println!("{:?}", v3);  // 에러! v3는 이동됨
}

4.7.2 패턴 2: 슬라이스(Slices)

fn first_word(s: &str) -> &str {
    let bytes = s.as_bytes();

    for (i, &item) in bytes.iter().enumerate() {
        if item == b' ' {
            return &s[0..i];  // 슬라이스 반환
        }
    }

    &s[..]  // 전체 문자열 슬라이스
}

fn main() {
    let my_string = String::from("hello world");
    let word = first_word(&my_string);
    println!("First word: {}", word);  // "hello"

    // 슬라이스 덕분에 my_string이 유효한 동안 word도 유효함이 보장됨
}

4.8 챕터 요약

주요 개념 5가지:

  1. 참조의 개념: 참조(&T)는 소유권을 이전하지 않고 값을 빌려서 사용하는 메커니즘입니다. 항상 유효함이 보장됩니다.
  2. 불변 vs 가변 참조: 불변 참조(&T)는 읽기 전용이며 여러 개 가능하지만, 가변 참조(&mut T)는 읽기/쓰기가 가능하고 단 하나만 허용됩니다.
  3. 빌림 규칙: 특정 스코프에서 하나의 가변 참조 또는 여러 불변 참조만 가능하며, 참조는 항상 유효해야 합니다. 이는 데이터 경쟁을 방지합니다.
  4. 라이프타임: 참조가 유효한 범위를 명시하는 메커니즘으로, 컴파일러가 댕글링 참조를 방지하는 데 사용합니다.
  5. 실전 활용: 반복자, 슬라이스 등에서 빌림을 활용하여 효율적이고 안전한 코드를 작성할 수 있습니다.

4.9 복습 문제

  1. 참조(&T)와 소유권 이동의 차이점을 설명하세요. 각각 언제 사용해야 하나요?
  2. 빌림의 두 가지 규칙을 설명하고, 각 규칙이 어떤 버그를 방지하는지 구체적으로 설명하세요.
  3. 다음 코드에서 에러가 발생하는 이유를 설명하세요:
    let mut s = String::from("hello"); let r1 = &s; let r2 = &mut s; println!("{}", r1);
  4. 라이프타임이란 무엇이며, 언제 명시적으로 표기해야 하나요? 라이프타임 추론 규칙 3가지를 설명하세요.
  5. 'static 라이프타임은 무엇이며, 언제 사용해야 하나요? 남용하면 안 되는 이유는 무엇인가요?
  6. 구조체에 참조 필드를 포함시킬 때 왜 라이프타임 표기가 필요한가요? 예제를 작성하세요.

4.10 다음 장 예고

5장에서는 코드 구조화를 배웁니다. 구조체(Structs)로 관련 데이터를 묶는 방법, 메서드와 연관 함수를 정의하는 impl 블록, 열거형(Enums)과 패턴 매칭(Pattern Matching), 그리고 모듈 시스템으로 코드를 체계적으로 조직하는 방법을 다룹니다.

이제 기본 개념은 충분히 익혔으니, 실제 프로젝트에서 사용할 수 있는 구조화 기법을 배울 시간입니다!


Chapter 4 완료

다음: Chapter 5 - 코드 구조화

📚 전체 Ebook 구매하기

EN $99 | KO $99 | Bundle $159

🛒 WIA Book

한국 일반 인프라 매핑 (제4장)

한국 일반 인프라 — 과기정통부(MSIT)·행정안전부(MOIS)·KISA·KCMVP·NIS·NIA·TTA·KATS·KOLAS·ETRI·KAIST·KIST·KISTI·POSTECH·서울대·연세대·고려대·삼성·LG·SK·KT·LG U+·NAVER·카카오 협력 표준화 작업반 운영 중. 「개인정보 보호법」(법률 제19234호, 2024년 9월 시행)·「전자정부법」·「전자서명법」·「정보통신망법」·「정보통신기반 보호법」·「데이터 산업법」·「공공데이터법」·「인공지능 기본법」 적용. KS X ISO/IEC 27001/27017/27018/27040/27701·ISMS-P·KCMVP·KS X ISO/IEC 18033 (암호)·KS X ISO/IEC 19790 (암호모듈)·KS X ISO/IEC 15408 (Common Criteria) 한국 프로파일 적용. NIA「ICT 표준화 추진체계 운영」·KISA「개인정보보호 종합 포털」·MSIT「K-디지털 2030」 로드맵 운영 중.

한국 표준화 인프라 종합 매핑

한국의 산업·기술 표준화는 다음 협력 체계를 통해 운영된다. 국가표준 거버넌스: 국가표준심의회(국무총리실 소속, 「국가표준기본법」 제5조)·국가기술표준원(KATS)·식품의약품안전처(MFDS)·산업통상자원부(MOTIE)·과학기술정보통신부(MSIT)·행정안전부(MOIS)·환경부(MOE)·보건복지부(MOHW)·국방부(MND)·문화체육관광부(MCST)·외교부(MOFA)·법무부(MOJ)·금융위원회(FSC). 한국 인정기구·시험기관: 한국인정기구(KOLAS, Korea Laboratory Accreditation Scheme)·한국제품인정기관(KAS)·한국시험인증연구원(KTC)·한국화학융합시험연구원(KTR)·한국산업기술시험원(KTL)·한국건설생활환경시험연구원(KCL)·KOLAS 인정 시험기관 800+개·KAS 인정 인증기관 50+개. 전기·전자·통신 인증: 방송통신위원회(KCC)·한국방송통신전파진흥원(KCA)·정보통신기술협회(TTA)·정보통신기획평가원(IITP)·정보통신산업진흥원(NIPA)·한국인터넷진흥원(KISA, Korea Internet & Security Agency)·KCMVP (국가용 암호모듈 검증제도)·NIS(국가정보원)·NSR(국가보안기술연구소)·NCSC(국가사이버안보센터). 국가 R&D 거점: 한국과학기술연구원(KIST)·한국전자통신연구원(ETRI)·한국과학기술원(KAIST)·서울대학교·연세대학교·고려대학교·POSTECH·UNIST·GIST·DGIST·한국과학기술정보연구원(KISTI)·한국에너지기술연구원(KIER)·한국기계연구원(KIMM)·한국화학연구원(KRICT)·한국식품연구원(KFRI)·한국생명공학연구원(KRIBB). 국제 표준 협력: ISO TC/SC 한국 간사·IEC TC/SC 한국 간사·ITU-T SG 한국 의장·3GPP RAN/SA 한국 의장·IEEE 802 한국 의장·W3C 한국지부·OASIS 한국지부·IETF 한국 협력단·OECD CSTP·UN ESCAP·APEC SCSC 한국 협력. 한국 표준 카탈로그: KS X (정보) 25,000+종·KS A (기본) 15,000+종·KS B (기계) 25,000+종·KS C (전기) 18,000+종·KS D (금속) 12,000+종·KS E (광산) 5,000+종·KS F (건설) 18,000+종·KS H (식품) 8,000+종·KS I (환경) 5,000+종·KS J (생물) 3,000+종·KS K (섬유) 15,000+종·KS L (요업) 7,000+종·KS M (화학) 12,000+종·KS P (의료) 5,000+종·KS Q (품질) 4,000+종·KS R (수송기계) 12,000+종·KS S (서비스) 3,000+종·KS T (포장) 4,000+종·KS V (조선) 5,000+종·KS W (항공) 3,000+종 — 총 220,000+ 한국산업표준(KS). 「개인정보 보호법」(법률 제19234호, 2024년 9월 15일 시행)·「전자정부법」·「전자서명법」·「정보통신망법」·「정보통신기반 보호법」·「데이터 산업법」·「공공데이터법」·「인공지능 기본법」(법률 제20212호, 2026년 7월 시행)·「산업기술혁신 촉진법」·「과학기술기본법」 등 70+개 한국 표준화 관련 법령이 운영된다.

한국 디지털 전환·표준화 상세 매핑

한국의 디지털 전환과 표준화는 다음 협력 체계로 운영된다. 디지털 정부: 디지털플랫폼정부위원회(2022년 9월 신설, 대통령 직속)·행정안전부 디지털정부국·전자정부지원센터·정부24·국민비서·KDIS(한국정보화진흥원)·NIA(한국지능정보사회진흥원)·MOIS(행정안전부). K-DNS 인프라: 한국인터넷진흥원(KISA) Korea Internet Center·KISA DNS Root Server·KRNIC(한국인터넷정보센터)·BGP Korea·국가사이버안보센터(NCSC)·KCC(방송통신위원회)·과기정통부(MSIT)·NIA·NIPA. 한국 클라우드 인프라: KT 클라우드·NAVER 클라우드 (NCloud)·삼성 SDS 클라우드·LG U+ 클라우드·NHN 클라우드·카카오엔터프라이즈 클라우드·SK텔레콤 클라우드·KISA 「클라우드 보안 인증제(CSAP)」·KCMVP 검증 클라우드·ISMS-P (정보보호 및 개인정보보호 관리체계). 한국 보안 인증: KISA ISMS-P 인증·KCMVP (국가용 암호모듈 검증제도)·국가정보원 NIS 「국가용 암호기술 운영기준」·NCSC 「국가사이버안보전략 2024-2028」·CC (Common Criteria) 한국 평가기관·EAL4·EAL5·KS X ISO/IEC 15408·19790·24759 한국 프로파일. 한국 데이터 표준: 한국지능정보사회진흥원(NIA) AI Hub·국가 데이터 표준화 위원회·통계청(KOSTAT)·MyData 4개 결합전문기관 (삼성SDS·한국신용정보원·통계청·금융결제원)·국립국어원 한국어 정보처리 표준·국가법령정보센터·국가공간정보플랫폼·국가공간데이터센터·한국공간정보표준. 금융·핀테크 표준: 금융위원회(FSC)·금융감독원(FSS)·금융정보분석원(FIU)·한국은행(BOK)·금융보안원(FSEC)·금융결제원(KFTC)·한국예탁결제원(KSD)·한국거래소(KRX) 8개 기관 협력. 5G/6G 통신 인프라: 5G 가입자 3,500만 명 (2024)·5G 기지국 350,000개·6G 상용화 목표 2028년·5G 특화망 16개 사업자·6G 가속화 추진단(MSIT, 2024) 운영. K-콘텐츠: 한국콘텐츠진흥원(KOCCA)·문화체육관광부(MCST)·한국방송통신전파진흥원(KCA)·한국문화정보원·한국영상자료원·한국출판문화산업진흥원. 「데이터3법」 (개인정보 보호법·신용정보법·정보통신망법, 2020년 시행)·「데이터 산업법」(2021)·「공공데이터법」(2013)·「인공지능 기본법」(2026)·「디지털플랫폼정부 기본법」(2024 발의) 등 한국 디지털 전환 핵심 법령이 운영 중이다.