[Rust] 소유권
러스트는 소유권이라는 개념을 통해 C++처럼 직접 메모리 할당/해제를 하거나 JS처럼 가비지 컬렉터를 사용하지 않아도 메모리를 관리할 수 있다.
스택
- 꼭대기(Top)에 있는 값을 가져와서 쓰면 되기에 데이터 공간을 검색하지 않아도 된다.
- 스택에 담긴 모든 데이터는 크기가 고정되어있다.
그래서 스택에 저장된 데이터에 빠르게 접근할 수 있다.
힙
가변크기의 데이터를 담기 위해 힙을 사용한다.
힙 메모리 할당 시 운영체제가 자유체인 속 빈 공간을 찾아다녀야 하기 때문에 데이터 접근이 느리다.
힙 데이터를 관리하기 위해 소유권이 존재한다.
소유권 규칙
- 모든 값은 오너(Owner)라고 불리는 변수가 있다.
- 한 번에 딱 하나의 오너만 존재할 수 있다.
- 오너가 스코프 밖으로 벗어나면, 값이 버려진다.
변수의 스코프
{ // s 없음
let s = "hello"; // s 생김
// s 가지고 뭔가 함
}
// 스코프 끝나서 s가 없어짐
러스트의 문자열
1. 스트링 리터럴
하드코딩된 문자열, 변경 불가.
아마 스택에 저장될 듯?
스트링 리터럴은 힙도 스택도 아닌 프로그램 바이너리에 저장된다.
2. String
타입
힙에 할당되고 변경 가능
let s = String::from("hello");
소유권 이전(move)
한번의 allocate
와 한번의 free
가 쌍을 이뤄야 한다.
스코프에 벗어난 변수는 drop
이라는 함수를 호출하여 메모리를 반환한다.
그렇다면 아래 코드의 경우
let s1 = String::from("hello");
let s2 = s1;
s1
과 s2
가 스코프 밖으로 벗어나면 하나의 할당 메모리에 두번 해체가 호출되어 메모리 손상이 일어날 수 있다.
러스트에서는 할당되는 메모리를 복사하는 대신에 s1
의 소유권을 s2
로 이동시킨다.
스코프를 벗어날 떄 s1
을 해제할 필요가 없게 만든다.
클론(Clone)
clone
메서드로 힙 데이터를 깊은 복사할 수 있다.
아래 코드의 s1
과 s2
둘 다 유효하다.
let s1 = String::from("hello");
let s2 = s1.clone();
println!("s1 = {}, s2 = {}", s1, s2);
스택에만 있는 데이터는 복사된다.
정수형과 같이 컴파일 시 크기가 결정되는 타입은 모두 스택에 저장된다.
복사본을 빠르게 만들 수 있기에 깊은 복사와 얕은 복사 간의 차이가 없다.
Copy
트레잇을 가진 타입들은 아래와 같이 값이 이동하는 대신 복사된다.
let x = 5;
let y = x;
println!("x = {}, y = {}", x, y);
함수에서 소유권
대입과 마찬가지로 함수에 변수를 넘길 때 값의 소유권도 넘어가거나 값이 복사된다.
함수를 사용하면서 소유권을 줬다가 다시 돌려받는 것은 매우 귀찮다.
함수에가 값을 사용할 수 있도록 하면서 소유권을 갖지 않도록 하기 위해 참조자라는 기능을 사용한다.