Skip to content

Latest commit

 

History

History
124 lines (98 loc) · 3.5 KB

ownerShip.md

File metadata and controls

124 lines (98 loc) · 3.5 KB

OwnerShip

Rust는 '소유권' 모델을 통해 메모리 안정성을 높힌 언어이다.

소유권은 메모리의 소유자가 메모리를 정리하게 됨을 의미하며 소유자란 함수이다. 메모리는 소유자로부터 이동하거나 대여할 수 있다.

enum Name {
  Chul,
}

fn my_fn(data: Name) {
  match data {
    Name::Chul => println!("Hi"),
    _ => println!("Exit"),
  }
}
fn main() {
  let my_data = Name::Chul;
  my_fn(my_data);
  my_fn(my_data); // Error: use of moved value
}

위의 코드는 알반적으로 전혀 문제가 없어 보이지만 my_fn 함수를 두 번째 호출 했을 때에는 에러가 발생한다.

이유는 main 함수의 my_data의 소유권이 my_fn으로 넘어갔고 "메모리의 소유자가 메모리를 정리해야 하는" 원칙으로 첫 번째 my_fn 함수가 종료 될 때 my_data 변수가 메모리에서 지워져 버렸기 때문이다.

따라서 두 번째 my_fn 함수를 호출하면 사용된 변수를 호출했다고 에러가 발생한다. 상당히 흥미로운 부분이다.

fn my_fn(data: &Name) {
  match data {
    Name::Chul => println!("Hi"),
    _ => println!("Exit"),
  }
}
fn main() {
  let my_data = Name::Chul;
  my_fn(&my_data);
  my_fn(&my_data);
  // Hi Hi
}

"메모리는 소유자로부터 이동하거나 대여할 수 있다"고 했다. 이동을 하게 되면 다음 소유권자가 삭제해야 하지만 대여를 하게 되면 정리하지 않아도 된다.

따라서 & 표시를 앞에 붙여 해당 데이터를 대여(참조)한다고 표시해 위의 문제를 해결할 수 있다.

또한 메모리의 소유권이 넘어갈 때 데이터의 값을 복사해서 가져가므로 비효율적이다. 대여를 사용하면 데이터가 한곳에 머물러 있기 때문에 더 효율적인 동작을 하게 된다.

fn sub() {
  let my_sub_data = 123;
}
let main() {
  let my_main_data = 123;
}

메모리의 소유는 "선언"되었을 때 정해진다. 따라서 my_sub_datasub 함수가 소유하고 있으며 my_main_datamain 함수가 소유하고 있다.

with filter

let a: Option<i32> = Some(1);
let a_filtered = a.filter(|num| num == 1); // Error! can't compare `&i32` with `{integer}`
let a_filtered = a.filter(|num| num == &1); // OK

클로저에서 값을 비교할 때도 소유권을 신경 써야 한다. 위 예에서는 filter 함수를 사용할 때 원소 num을 빌려와서 쓰므로 타입을 맞춰야 한다.

Lifetime

#[derive(Debug)]
enum Answer {
  Yes,
  No,
}

#[derive(Debug)]
struct Form {
  question: &Answer, // Error! missing lifetime specifier.
}

fn main() {
  let form;
  {
    let answer = Answer::Yes;
    form = Form { question: &answer };
  }
  println!("{:?}", form);
}

위 코드는 정상적으로 실행되지 않는다. 중괄호가 끝나면서 answer은 삭제되지만 form 변수에서 answer 값을 계속 빌려야 하기 때문이다.

따라서 이러한 경우를 체크하기 위해 수명 주석을 추가해야 한다.

#[derive(Debug)]
enum Answer {
  Yes,
  No,
}

// 수명이 필요한 주석은 'abc... 등으로 작성한다.
#[derive(Debug)]
struct Form<'a> {
  question: &'a Answer,
}

fn main() {
  let form;
  {
    let answer = Answer::Yes;
    form = Form { question: &answer }; // Error! answer does not live long enough.
  }
  println!("{:?}", form);
}

이제 정상적으로 에러가 노출된다. answer를 중괄호 밖으로 가져가야 한다.