Daily Rust Study - 소유권

Koowater·2024년 8월 13일
0
post-thumbnail

참고한 자료 - The Rust Programming Language : 소유권이 뭔가요?

  • 프로그래밍 언어마다 다양한 방식으로 메모리를 관리하는데, 크게 세 가지로 구분할 수 있습니다.

    1. Garbage collector : 사용되지 않는 메모리를 주기적으로 찾아 자동으로 해제 (Java)
    2. 직접 관리 : 필요할 때 메모리를 직접 할당하고 필요하지 않아진 경우 직접 해제 (C)
    3. 소유권 : 메모리에 단 하나의 소유자를 부여하고, 소유자가 사라진 경우 메모리는 자동으로 해제 (Rust)
  • stack 영역과 heap 영역에 대해 먼저 파악하면 소유권을 이해하는데 큰 도움이 됩니다.

    • stack 영역
      • 후입선출(First in, Last out)
      • 효율적이며 힙 영역보다 빠름
      • stack 영역에 저장될 값들은 크기가 정해져 있어야 함
      • 함수를 호출하면 호출한 함수에 넘겨준 값과 해당 함수의 지역변수들이 stack 영역에 push됨
      • 데이터가 서로 인접하여 접근이 빠름
    • heap 영역
      • 데이터를 저장하고 싶은 경우 저장할 공간이 있는지 먼저 확인이 필요 (운영체제가 이를 담당)
      • 공간이 충분하다면 사용할 공간을 가리키는 pointer를 반환
      • 이렇게 heap 영역 내 사용할 메모리 공간을 확인하고 pointer로 사용할 공간을 확보하는 과정을 allocation(할당)이라 부름
      • stack 영역을 사용하는 것은 allocation이 아님
      • heap 영역은 데이터가 서로 멀리 떨어져있고 포인터를 통한 참조로 데이터에 접근해야 하기 때문에 stack 영역보다 느림
  • Rust의 소유권 규칙은 아래와 같습니다.

    • 각각의 값은 소유자가 정해져 있으며, 하나의 소유자는 하나의 값만 소유할 수 있습니다. (각각의 값엔 각각 하나의 소유자에게만 소유될 수 있다.)
    • 소유자가 스코프 밖으로 벗어날 때, 값은 버려집니다. (dropped)
    {                     // s는 아직 선언되지 않아서 여기서는 유효하지 않습니다.
        let s = "hello";   // 이 지점부터 s가 유효합니다.
    
        // s로 어떤 작업을 합니다.
     }                      // 이 스코프가 종료되었고, s가 더 이상 유효하지 않습니다.
  • 소유권 관리 이해의 중요성

    • stack 영역에서 다루는 데이터의 경우 빠르게 복사할 수 있기 때문에 변수 간에 자유롭게 바인딩이 가능합니다.
    • heap 영역에서 다루는 데이터의 경우 pointer로 관리되기 때문에 변수 간의 바인딩에 제한이 있습니다. 객체의 소유권을 넘겨주는 방식으로 변수 간의 데이터를 넘겨줘야 합니다. 변수 간에 데이터를 복사하고자 하는 경우 clone 메소드를 사용해 pointer가 가리키는 값까지 복사해 전달(deep copy)해야 합니다. 이 경우 overhead가 발생할 수 있습니다. Rust는 어떠한 경우에도 자동으로 deep copy를 수행하지 않습니다.
    • Rust에는 문자열을 두 가지 타입으로 다룹니다.
      • String literal (문자열 리터럴)
        • 문자열 리터럴은 고정된 크기의 메모리 영역을 사용합니다. stack 영역에서 사용되는 데이터 형식입니다.
      • String object (문자열 객체)
        • 문자열 객체는 고정된 크기의 메모리 영역을 사용하지 않기 때문에 문자열의 길이가 줄어들거나 늘어날 수 있습니다. heap 영역에서 사용되는 데이터 형식입니다.
      • 이와 같이 다루는 데이터의 타입이 stack 영역에서 다뤄지는지 heap 영역에서 다뤄지는지에 따라 다루는 방법과 성능에서 큰 차이를 보입니다.
  • 데이터가 어떤 영역에서 다뤄지는지 구분하는 방법

    • Copy trait가 구현되어 있는 경우 stack 영역에서 다뤄지는 데이터 타입입니다. 자유롭게 복사되며 대입 연산에도 사용할 수 있습니다.
      • 정수형 혹은 실수형 타입, 논리 자료형, 문자 타입 char, Copy 가능한 타입만으로 구성된 tuple
    • Drop trait가 구현되어 있는 경우 heap영역에서 다뤄지는 데이터 타입입니다. 데이터는 소유권에 의해 이동되며 대입 연산 시 clone 메소드로 복사되어야 합니다.
      • String 등 할당이 필요한 데이터 타입, Copy가 불가능한 타입이 포함된 tuple
  • 소유권과 함수

    • heap 영역의 객체의 경우 함수에 인자로 전달된 경우 소유권을 함수에 넘겨주게 됩니다. 함수 호출이 종료된 경우 함수 내에서 소유권을 잃기 때문에 사용할 수 없습니다.
    • stack 영역의 객체의 경우 함수에 인자로 전달되더라도 값을 복사하여 전달합니다. 함수 호출이 종료된 후에도 자유롭게 사용할 수 있습니다.
  • heap 영역의 데이터를 함수에 전달하였다가 다시 사용하려면 함수에서 다시 반환받아 사용하는 방법이 있습니다. 그러나 이 방법은 매우 비효율적입니다. 그래서 Rust는 참조자(reference)라는 기능을 지원합니다.

  • stack 영역과 heap 영역의 관계는 mutable과 immutable의 관계라고도 볼 수 있나요?

    • 아닙니다. 정수형 변수라도 mutable인지 immutable인지 별도로 지정할 수 있으며 String 객체도 마찬가지입니다. Rust는 기본적으로 변수를 immutable로 선언하며 이는 안전성, 명확성, 최적화 등의 이유입니다.
profile
Speech to Text를 공부하고 있습니다.

0개의 댓글