Rust여행 2차시 - 핵심개념

포비·2024년 12월 3일

알아보자

목록 보기
5/111
post-thumbnail

소유권(Ownership)은 러스트에서 메모리 안전성을 보장하는 핵심 개념입니다. 러스트의 소유권 시스템은 누군가가 메모리를 소유하고, 다른 사람이 그 메모리 자원을 변경할 때마다 명시적으로 이를 추적하도록 만들어, 메모리 오류를 방지합니다. 소유권은 러스트의 가장 중요한 특징 중 하나이며, 프로그램이 더 안전하고 효율적으로 동작하도록 도와줍니다.


1. 소유권의 기본 개념

  • 소유자(Owner): 변수는 특정 데이터를 "소유"하게 됩니다. 변수는 해당 데이터를 메모리에서 관리하는 책임을 집니다.
  • 소유권 이전 (Move): 변수를 다른 변수에 할당하거나, 함수에 인자로 전달할 때 데이터의 소유권이 이전됩니다. 소유권이 이전된 후에는 이전 변수는 그 데이터를 더 이상 사용할 수 없습니다.
  • 복사 (Copy): Copy가 가능한 타입들은, 값을 복사할 때 소유권이 이전되지 않습니다. 예를 들어, 기본적인 숫자 타입(i32, u8 등)은 복사가 가능하여, 여러 변수에서 동일한 데이터를 가질 수 있습니다.

2. 소유권 규칙

  • 하나의 값에 대해 하나의 소유자만 존재: 러스트는 메모리 관리를 자동으로 처리하지만, 변수는 한 번에 하나의 소유자만 가질 수 있습니다. 소유권을 넘기면 원래 변수는 더 이상 데이터를 사용하지 못합니다.
  • 소유권은 함수 호출 시 이동: 함수에 인자를 전달하면, 기본적으로 소유권이 이동합니다.

예시: 소유권 이동

fn main() {
    let s1 = String::from("Hello"); // s1이 "Hello"를 소유
    let s2 = s1; // 소유권이 s1에서 s2로 이동 (s1은 더 이상 사용 불가)
    println!("{}", s1); // 오류: s1은 이미 소유권이 이동되어 사용 불가
}

이 예시에서 s1String 데이터를 소유하고 있고, s1s2에 할당할 때, s1의 소유권이 s2로 이동합니다. s1을 다시 사용하려 하면 오류가 발생합니다.

3. 빌림(Borrowing)

소유권을 넘기지 않고 참조를 통해 데이터를 빌릴 수 있습니다. 빌림은 불변 참조(immutable borrow)가변 참조(mutable borrow)로 나뉩니다.

  • 불변 참조: 데이터를 변경할 수 없고, 여러 번 참조할 수 있습니다.
  • 가변 참조: 데이터를 변경할 수 있지만, 동시에 한 번에 하나의 가변 참조만 존재할 수 있습니다.

불변 참조 예시

fn main() {
    let s1 = String::from("Hello");
    let s2 = &s1; // 불변 참조
    println!("{}", s1); // 불변 참조는 원본 데이터를 변경할 수 없음
}

### 가변 참조 예시
fn main() {
    let mut s1 = String::from("Hello");
    let s2 = &mut s1; // 가변 참조
    s1.push_str(", world!"); // 가변 참조를 통해 값을 변경할 수 있음
    println!("{}", s1);
}

### 4. **소유권의 문제를 해결하는 라이프타임(Lifetime)**

소유권 시스템은 라이프타임과 밀접한 관계가 있습니다. 라이프타임은 참조가 유효한 기간을 명시적으로 지정하여, 참조가 더 이상 유효하지 않게 되는 경우를 방지합니다.

5. 소유권의 장점

  • 메모리 안전성: 컴파일 타임에 소유권 규칙을 체크함으로써, 런타임에서 발생할 수 있는 메모리 오류를 방지합니다. 예를 들어, 중복 해제(double free)유효하지 않은 메모리 접근(use-after-free)을 방지합니다.
  • 자동 메모리 관리: 러스트는 가비지 컬렉터를 사용하지 않으면서도, 소유권을 통해 자동으로 메모리를 관리합니다. 이로 인해 성능 손실 없이 안전성을 보장할 수 있습니다.

Copy 트레잇은 값을 복사할 수 있는 타입에 적용됩니다. Copy 트레잇을 구현한 타입들은 소유권이 이동하지 않고, 데이터를 복사하기 때문에 여러 변수에서 동일한 값을 공유할 수 있습니다.

6. Copy 트레잇 예시


fn main() {
    let x = 5;  // i32는 Copy 트레잇을 구현한 타입
    let y = x;  // x의 값이 복사됨, 소유권 이동 없음

    println!("x: {}", x);  // 여전히 x를 사용할 수 있음
    println!("y: {}", y);  // y도 복사된 값을 가짐
}
  • i32와 같은 간단한 숫자 타입Copy 트레잇을 구현합니다. 그래서 xy에 할당하면 x의 값이 복사되어 y가 그 값을 갖게 됩니다.
  • 이 경우 소유권이 이동하지 않으며, xy는 각각 독립적인 변수로 존재할 수 있습니다. 그래서 x를 사용해도 문제가 발생하지 않습니다.

7. Copy가 적용되지 않는 예시

반면, String이나 힙에 할당된 데이터Copy 트레잇을 구현하지 않기 때문에 소유권이 이동합니다.

fn main() {
    let s1 = String::from("Hello");
    let s2 = s1;  // s1의 소유권이 s2로 이동

    println!("{}", s1);  // 오류: s1은 더 이상 유효하지 않음
}

이렇게 String과 같은 동적 할당 데이터는 Copy를 구현하지 않아 소유권이 이동하며, 소유권 이동 후에는 원래 변수(s1)를 사용할 수 없게 됩니다.

결론

소유권 시스템은 러스트에서 메모리 안전성을 보장하는 핵심 개념입니다. 소유권을 통해 데이터가 어떻게 관리되는지, 빌림을 통해 안전하게 참조할 수 있는 방법을 배워두면, 메모리 오류를 방지하며 안정적인 프로그램을 작성할 수 있습니다.

profile
무엇이든 필요한 것을 합니다. https://mint-middle-1e5.notion.site/2b7655e8316980ad9422d96a6f3947de

0개의 댓글