Rust References and Borrowing (참조와 빌림)

고승우·2023년 6월 3일
0
post-thumbnail

Rust References and Borrowing 공식 doc

References and Borrowing (참조자와 빌림)

fn main() {
    let s = String::from("hello");  // s comes into scope

    takes_ownership(s);             // s's value moves into the function...
                                    // ... and so is no longer valid here

    let x = 5;                      // x comes into scope

    makes_copy(x);                  // x would move into the function,
                                    // but i32 is Copy, so it's okay to still
                                    // use x afterward

} // Here, x goes out of scope, then s. But because s's value was moved, nothing
  // special happens.

fn takes_ownership(some_string: String) { // some_string comes into scope
    println!("{}", some_string);
} // Here, some_string goes out of scope and `drop` is called. The backing
  // memory is freed.

fn makes_copy(some_integer: i32) { // some_integer comes into scope
    println!("{}", some_integer);
} // Here, some_integer goes out of scope. Nothing special happens.

위 코드 같은 경우, String 은 takes_ownership() 으로 이동하면서 함수 내에서 사용이 가능하지만, takes_ownership() 함수가 끝나면 String이 scope 밖을 벗어나면서 drop 된다. 이를 대신해서 우리는 String의 reference(참조)를 인자로 넘겨줄 수 있다.

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()
}

ampersand(&) 기호 참조를 나타내고, 어떤 값의 소유권을 넘기지 않고 참조할 수 있도록 해준다. 이런 방식으로 참조자를 만드는 것을 Borrowing(빌림)이라고 한다.

* 기호를 사용하여 dereference 역참조를 할 수 있다.
reference하는 값을 변경할 수 없다. 변경해야 한다면 이후에 나오는 mutable reference를 사용해야 한다.

fn main() {
    let s = String::from("hello");
    change(&s);
}

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

Mutable References (가변 참조자)

먼저 smut로 바꿔야 한다. &mut s로 가변 참조자를 생성하고 some_string: &mut String으로 이 가변 참조자를 받아야 한다. 하지만 가변 참조자는 한가지 큰 제한이 있다. 가변 참조자를 만들면, 해당 값에 대한 참조자를 더 이상 만들지 못한다. 반대로 이미 참조자가 존재한다면, 가변 참조자를 만들 수 없다. 이러한 방식으로 compile하는 동안 data race를 방지한다.

code1) error

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);

code2) success

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);

reference 값은 stack에 저장되기 때문에 소유권에 대한 문제는 고려하지 않아도 된다. 위 코드에서 r1r2의 lifetime은 r3 이전에 끝나기 때문에, 가변 참조자를 선언할 수 있다.

Dangling References (댕글링 참조자)

Dangling pointer란, 어떤 메모리를 가리키는 포인터는 존재하지만, 해당 메모리는 해제한 경우에 생기는 포인터를 말한다. 하지만 러스트에서는 모든reference들이 dangling reference가 되지 않도록 보장한다.

fn main() {
    let reference_to_nothing = dangle();
}

fn dangle() -> &String { // dangle returns a reference to a String
    let s = String::from("hello"); // s is a new String
    &s // we return a reference to the String, s
} // Here, s goes out of scope, and is dropped. Its memory goes away.
  // Danger!

sdangel 내부에서 생성되었기 때문에, sdangle에 함께 drop된다. 만약 String의 소유권을 main으로 넘기고 싶었다면, 함수를 아래 코드로 수정하면 가능하다.

fn no_dangle() -> String {
    let s = String::from("hello");
    s
}
profile
٩( ᐛ )و 

0개의 댓글