티스토리 뷰

Rust-Language

참조자와 빌림

kmj24 2021. 4. 3. 12:51

참조자(references)

다음의 코드는 함수에 참조자를 인자값으로 넘겨준다.

pub fn references(){
    let s1 = String::from("hello");
    let len = calculate_length(&s1);
    println!("{}, {}", s1, len);
}

fn calculate_length(s: &String) -> usize{
    return s.len();
}

여기서 calculate_length 함수에 s1을 넘기는게 아닌 s1의 참조자를 넘겼기 때문에 함수 호출 이후에도 s1을 그대로 사용할 수 있다.

참조자를 활용하면 소유권을 넘기지 않고 값을 참조할 수 있다.

&이 참조자이다

참조자가 가리키는 값은 참조자가 스코프를 벗어났을 때에도 메모리가 해제되지 않는다.

fn calculate_length(s: &String) -> usize{
    return s.len();
}

위의 코드에서 parameter인 s의 스코프는 함수 파라미터의 스코프와 동일하지만 참조자이므로 소유권이 없고, 함수가 종료되더라도 아무런 일이 발생하지 않는다. 소유권을 반환하기위한 메모리 해제를 할 필요가 없다는 의미다.

 

함수의 파라미터로 참조자를 만드는 것은 borrowing(빌림) 이라고 부른다.

참조자는 참조하는 값을 변경할 수 없다.

만약 borrowing한 값을 수정하려고한다면 다음과 같은 오류를 만나게 될 것이다.

pub fn references(){
    let s1 = String::from("hello");
    change(&s1);
}

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

가변 참조자(mutable references)

가변 참조자를 사용한다면 참조자도 값을 변경할 수 있다.

pub fn references(){
    let mut s = String::from("hello");
    change(&mut s);
}
fn change(s: &mut String){
    s.push_str(", world");
}

이런 형태로 사용하면 참조하는 값을 변경할 수 있다.

가변 참조자는 특정 스코프내 특정 데이터 조각에 대한 가변 참조자를 딱 하나만 만들 수 있다.

(불변 참조자는 여러개를 만들더라도 문제가 없음)

아래의 코드에서 하나의 변수 s에 대한 가변 참조자를 2개 선언 했을때 오류가 발생한다.

//참조자와 빌림
pub fn references(){
    let mut s = String::from("hello");
    let r1 = &mut s;
    let r2 = &mut s;
    println!("{}, {}", r1, r2);
}

러스트 언어가 이렇게 가변 참조자에 대한 제한을 둔 이유는 data race를 방지할 수 있도록 하기 위함이다.

data race

 - 다수의 포인터가 동시에 같은 데이터에 접근

 - 하나 이상의 포인터가 데이터를 사용하려한다.

 - 해당 데이터 접근에 대한 어떠한 메커니즘도 없다.

이럴 경우 data race가 발생하며 이 경우 프로그램에 대한 취약점이 발생할 수있다.

이러한 data race를 방지하기 위해 제한을 두고 컴파일 단계에서 막아버린 것이다.

 

data race를 방지하면서도 여러개의 가변 참조자를 사용하고 싶은 경우, 새로운 스코프를 만들어서 여러개의 가변 참조자를 사용하도록 할 수 있다.

pub fn references(){
    let mut s = String::from("hello");
    {
        let r1 = &mut s;
        println!("{}", r1);
    }
    let r2 = &mut s;
    println!("{}",  r2);
}

또한 가변 참조자는 같은 스코프 내 불변참조자가 있더라도 혼용할 수 없다.

pub fn references(){
    let mut s = String::from("hello");
    let r1 = s;
    let r2 = &mut s;
    println!("{}", r1);
    println!("{}", r2);
}

 

댕글링 참조자(Dangling References)

포인터가 있는 C/C++언어에서는 댕글링 포인터를 만들게 될 수도 있다.

댕글링 포인터는 메모리를 가리키는 포인터를 가지고 있으면서, 그 메모리를 해제함으로써 다른 개체가 해당 메모리에 접근할 수 있을지도 모를 메모리를 참조하고 있는 포인터를 의미한다.

러스트언어는 컴파일러가 모든 참조자들이 댕글링 참조자가 되지 않도록 보장해준다.

만약 어떤 데이터의 참조자를 만들었을 경우, 컴파일러는 그 참조자가 스코프 밖으로 벗어나기 전에 데이터가 스코프 밖으로 벗어나지 않도록 확인해준다.

 

아래의 코드는 댕글링 참조자를 만드는 코드이다.

//참조자와 빌림
pub fn references(){
    let dangle = dangle();
}


fn dangle() -> &String{
    let s = String::from("hello");
    return &s; //s의 참조자를 반환하려고 한다
} 
//여기서는 s가 해제되었고, 해제된 s의 값을 참조하는 &s를 반환하려고 한다. 
//s는 메모리에서 해제되었고 해제된 메모리를 참조하려고 하므로 위험한 상태이다.

댕글링 참조자를 만들게 될 경우 컴파일 단계에서 사진과 같은 오류가 발생한다.

 

이 장의 전체 코드

pub fn references(){
    let s1 = String::from("hello");
    let len = calculate_length(&s1);
    println!("{}, {}", s1, len);
    let mut s = String::from("hello");
    {
        let r1 = &s;
        println!("{}", r1);
    }
    let r2 = &mut s;
    println!("{}", r2);
    change(&mut s);
    let dangle = dangle();
}

fn calculate_length(s: &String) -> usize{
    return s.len();
}
fn change(s: &mut String){
    s.push_str(", world");
}
//댕글링 참조자
fn dangle() -> &String{
    let s = String::from("hello");
    return &s;
}

 

참고 : rinthel.github.io/rust-lang-book-ko/ch04-02-references-and-borrowing.html

 

참조자와 빌림 - The Rust Programming Language

앞 절의 마지막에 등장한 튜플을 이용하는 이슈는 String을 호출하는 함수 쪽으로 반환함으로써 calculate_length를 호출한 이후에도 여전히 String을 이용할 수 있도록 하는 것인데, 그 이유는 String이 c

rinthel.github.io

 

 

'Rust-Language' 카테고리의 다른 글

구조체  (0) 2021.04.07
Slices  (0) 2021.04.05
소유권(Ownership)  (0) 2021.04.02
제어문  (0) 2021.03.30
함수  (0) 2021.03.30
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30 31
글 보관함