프로그래밍/프로그래밍언어

[rust] Ownership 3 - Reference

Hithero 2022. 1. 1. 22:26

C++에서 reference를 알고계시면 이해하기 조금 더 수월하다.

기존의 Ownership Rule은 string을 함수의 매개변수로 넘겨주게 되면 Ownership을 넘겨주는 것이라서, 함수가 끝나고 변수가 invalid되는 것이다.

이를 해결하기 위해 나온 개념이 reference이다. Ownership을 넘겨주는 것이 아닌, pointer를 넘겨주면서 함수이후 변수가 invalid가 되는 것을 방지한다.

fn main(){
	let s1 = String::from("hello");
    let len = calculate_length(&s1);
    println!("The length of '{}' is {}.",s1,len);
}
fn calculate_length(s : &String) -> usize {
	s.len()
}

기존처럼 calculate_length(s1)으로 reference로 제공하지 않는다면 s1 변수가 invalid가 되어 println!에서 해당 변수를 부르는 것이 문제가 있고, 컴파일시 에러가 발생할 것이다. 

하지만 reference로 넘겨주었기 때문에, 해당 주소값을 넘겨주는 것이다.

위의 코드에서 그림 (출처 : https://doc.rust-lang.org/book/ch04-02-references-and-borrowing.html)

위의 그림을 보듯이 s1을 가리키는 포인터 변수 s를 생성해내는 것이다.

함수에서 레퍼런스로 불러온 s변수는 read-only라고 생각하면 된다. 해당 함수내에서 변수의 변경이 발생한다면 컴파일 에러가 발생된다.

여러개의 reference를 만들어내는 상황도 존재하는 데, 멀티 프로그래밍에서 read/write lock과 비슷하다. 

    let mut s = String::from("hello");

    let r1 = &s; // no problem
    let r2 = &s; // no problem
    let r3 = &mut s; // BIG PROBLEM

    println!("{}, {}, and {}", r1, r2, r3);

위의 코드와 같이 r1,r2 와 같이 immutable referenec는 여러개 선언이 가능하다.(read lock) 하지만, r3와 같이 mutable reference를 선언하는 순간 컴파일 에러가 발생한다.(try to get write lock while read lock is activate) r1,r2의 레퍼런스의 값이 변경되면 안되기 때문이다.

하지만, r1,r2 선언없이 r3만 선언한다면 문제없이 컴파일 된다. 그리고 mutable reference 두개 이상 선언이 불가능하다. (write lock을 두개를 잡을 수 없는 것처럼)

    let mut s = String::from("hello");

    {
        let r1 = &mut s;
    } // r1 goes out of scope here, so we can make a new reference with no problems.

    let r2 = &mut s;

해당 코드는 정상적으로 컴파일된다. 왜냐면 r1의 scope가 벗어나면서 invalid가 되고, r2가 유일한 mutable reference이기 때문이다. 해당 코드에서 대괄호를 없애면 컴파일 에러가 발생한다. (아래의 코드와 같이)

    let mut s = String::from("hello");
    let r1 = &mut s;

    let r2 = &mut s; // compile error occur!

이러한 부분들이 있기때문에 변수의 lifetime(변수가 어느부분에서 사용되고 언제 이후로 사용되지 않는지)을 아는 것이 의미가 있다. 그리고 rust 컴파일러는 해당 부분을 파악해 mutable reference가 overlap되어 컴파일 에러가 발생되지 않게 하고자 한다. 변수의 lifetime을 설명에서는 Non-lexical Lifetimes(NLL)이라고 칭한다.

    let mut s = String::from("hello");

    let r1 = &s; // no problem
    let r2 = &s; // no problem
    println!("{} and {}", r1, r2);
    // variables r1 and r2 will not be used after this point

    let r3 = &mut s; // no problem
    println!("{}", r3);

NLL을 고려하지 않는 다면 r3 변수 선언은 immutable reference가 있는 상황에서 mutable referenec 선언이기 떄문에 컴파일 에러가 발생하겠지만, r1,r2 변수는 첫번째 println! 함수이후 사용되지 않기때문에 컴파일러에서 적절히 scope를 확인하고(이후에도 사용되는지) r3의 변수선언과 overlap이 되지 않는 다는 것을 파악한다. 그렇기 때문에 r3의 변수선언이 문제가 되지 않는 것이다.

728x90
반응형