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
}
먼저 s
를 mut
로 바꿔야 한다. &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
에 저장되기 때문에 소유권에 대한 문제는 고려하지 않아도 된다. 위 코드에서 r1
과 r2
의 lifetime은 r3
이전에 끝나기 때문에, 가변 참조자를 선언할 수 있다.
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!
s
가 dangel
내부에서 생성되었기 때문에, s
는 dangle
에 함께 drop된다. 만약 String의 소유권을 main으로 넘기고 싶었다면, 함수를 아래 코드로 수정하면 가능하다.
fn no_dangle() -> String {
let s = String::from("hello");
s
}