📌 "The Rust Programming Language"를 읽고 남긴 기록
모든 프로그램은 실행 중 자신이 사용하는 메모리를 관리하는 방법을 가져야한다. 실행 중 더이상 필요하지 않은 메모리를 수거하는 가비지 컬레터가 포함된 언어가 있는 반면에, 프로그래머가 명시적으로 메모리를 할당하고 해제해야하는 언어가 있다. Rust는 Ownership system을 도입하여 메모리를 관리한다.
변수가 유효한 범위를 scope라 한다.
{
let s = String::from("Hello world!"); // s is valid
} // scope now over, and s is no longer valid
let a = "This is string literal, &str typed";
let mut b = String::from("String instance!");
b.push_str(" append here")
String 타입의 값은
rust는 scope를 벗어난 변수의 메모리를 해제한다. 간단한 규칙이지만, 더 복잡한 상황에서는 행동을 예측하기 힘들 수 있다. 몇가지 상황을 통해 동작을 살펴보자.
let s1 = String::from("hello");
let s2 = s1;
rust의 String은 content를 가리키는 포인터, 문자열의 길이, 용량 세부분으로 구성되고 스택에 저장된다. 힙 메모리에는 문자열의 content가 저장된다.
s2에 s1을 대입하면, rust는 s1이 더이상 유효하지 않다고 여겨 s1을 통해 값에 접근하는 것을 허용하지 않는다. 이는 실제 값에 접근할 수 있는 능력을 다른 변수로 옮긴 것 처럼 보이며, 이러한 동작을 단순 복사와 구별해 move라 한다.
s1값이 s2로 move되었으므로 s1은 더이상 사용할 수 없으며, 따라서 다음과 같은 구현은 불가능하다.
let s1 = String::from("Hello");
let s2 = s1;
println!("{}", s1); // WRONG!!
clone메소드를 이용해 힙에 저장된 String data를 deeply copy할 수 있다.
let s1 = String::from("Hello);
let s2 = s1.clone();
println!("{}, {}", s1, s2);
i32와 같이 값이 스택에 저장되는 타입에 지정할 수 있는 Copy라는 특별한 trait이 있다. 값이 스택에 저장되고, 복사를 하면 값이 스택으로 그대로 복사된다(move 하지 않음).
함수 호출로 값을 전달하면 ownership이 호출한 함수의 인자로 옮겨진다. 따라서 함수 호출 이후에는 변수를 다시 사용할 수 없다.
fn main() {
let s = String::from("Hello world");
take_ownership(s);
println!("{}", s); // WRONG!!
}
fn take_ownership(s: String) {
println!("{}", s);
}
함수의 반환값을 받아 원래의 ownership을 되찾을 수 있다.
fn main() {
let s1 = String::from("Hello world);
let s2 = takes_and_gives_back(s1);
println!("{}", s2); // OK
}
fn takes_and_gives_back(s: String) -> String {
s
}