Rust 소유권 이동

mohadang·2023년 1월 21일
0

Rust

목록 보기
5/30
post-thumbnail

Rust 언어에서 가장 특이한 소유권 개념.
전통적인 언어가 함수 인자로 값을 전달 할 때 기본적인 동작이 얕은 복사나 깊은 복사를 수행한다
그러나 Rust는 소유권 이동을 한다.

struct MyBag {
    item: i32,
}

fn move_test(bag: MyBag) {
    println!("{}", bag.item);
}

fn main() {
    {
        let bag = MyBag { item: 32i32 };
        move_test(bag);
        println!("{}", bag.item);  
    }
    
    {
        let bag = MyBag { item: 32i32 };
        let bag2 = &bag;
        move_test(bag);
        println!("{}", bag2.item);
    }
}

그리고 위 코드를 컴파일 하면 다음과 같은 컴파일 에러를 발생 시킨다.

error[E0382]: borrow of moved value: `bag`
  --> src\main.rs:13:24
   |
11 |         let bag = MyBag { item: 32i32 };
   |             --- move occurs because `bag` has type `MyBag`, which does not implement the `Copy` trait
12 |         move_test(bag);
   |                   --- value moved here
13 |         println!("{}", bag.item);
   |                        ^^^^^^^^ value borrowed here after move
   |

Rust 컴파일러가 인스턴스의 생명 주기를 관리하여 문제가 발생할 경우 바로 에러를 발생 시킨다.
Rust는 적어도 소유권 이동을 안전하게 사용할 수 있는 환경을 준비를 한 셈이다.

다른 언어는 어떻까 ? C++에는 rvalue 연산자, 스마트 포인터와 함께 이동 연산에 대한 개념이 있다.

#include <iostream>
#include <memory>

void func(std::unique_ptr<int> pt) {
  std::cout << *pt << std::endl;
}

int main() {
  auto pt = std::make_unique<int>(10);
  func(std::move(pt));
  std::cout << *pt << std::endl;
  return 0;
}
g++ main.cpp -Wall

컴파일 경고 수준을 높여 보았지만 어떤 에러/경고가 없었다.
C++에서 소유권 이동에 대한 개념은 어떤 영역에서 활용 가치가 있는지 아직은 사례를 찾지 못하였다.

Rust의 C++ 소유권 이동과 다른 특이한 점은 stack 메모리에 대해서 소유권 이동이 가능 하다는 점이다

pub struct Foo {
    x: i32,
}

pub fn do_something(f: Foo) {
    f.x * 100;
    // f가 여기서 drop 됩니다
}

pub fn main() {
    let foo = Foo { x: 42 };
    // foo가 do_something으로 move 됩니다
    do_something(foo);
    // foo는 더 이상 사용할 수 없습니다
}

do_something 함수를 호출할때 별도의 스택 프레임을 생성하지 않았다.
C++과 같이 RVO 최적화가 발생 한것 같다.

하지만 이것만 보고 소유권 이동이 메모리 복사를 하지 않는다고 결단 내릴 수 는 없다.
전달될 메모리가 커지면 do_something 내에서도 스택 프레임을 생성하여 main 함수에서 전달된 인자를 복사 하는 모습을 볼 수 있따.

pub struct Foo {
    x: i64,
    y: i64,
    z: i64,
}

pub fn do_something(f: Foo)  {
    let s = f.x + f.y + f.z;
    // f가 여기서 drop 됩니다
}

pub fn main() {
    let foo = Foo {
        x: 1,
        y: 2,
        z: 3,
    };
    // foo가 do_something으로 move 됩니다
    do_something(foo);
    // foo는 더 이상 사용할 수 없습니다
}

main 함수에서 do_something 함수의 스택 프레임까지 미리 만들어 놓고 여기에 값을 복사 하는 것으로 보인다.

스택 메모리에서 소유권 이동을 할때 메모리 복사가 발생 할 수 있음을 유의해야 한다.

이 글 역시 stack 메모리의 소유권 이전은 복사를 통해 구현되고 있음을 확인할 수 있다
stack 메모리의 소유권 이전은 메모리 복사임을 명시해야 한다

profile
mohadang

0개의 댓글