rust c++ vector 객체 사용하기

wangki·2025년 2월 1일
0

Rust

목록 보기
11/55

문제

c++ 클래스 필드가 std::vector<특정 클래스>일 때, Rust에서 올바르게 사용하는 방법

해결책

autocxx 크레이트를 사용하면 된다.

다음과 같은 c++ 클래스가 있다고 가정하자.

class Goat {
public:
    Goat(uint32_t horns) : horns(horns) {}
    uin32_t get_horns() {
        return this-> horns;
    }
private:
    uin32_t horns;
}

class GoatWrapper {
public: 
    void set_goat_list() {
        test_list.push_back(Goat(30));  // 뿔의 개수 
        test_list.push_back(Goat(20));
        test_list.push_back(Goat(10));
    }

    const std::vector<Goat> get_goat_list() const {
        return this->goat_list;
    }
private:
    std::vector<Goat> goat_list;
};

필드 goat_list의 경우 Goat 클래스를 담고 있는 vector이다.
rust에서 다음과 같이 goat_wrapper 객체를 만든다.

let mut goat_wrapper = ffi::GoatWrapper::new().within_unique_ptr();
goat_wrapper.pin_mut().set_goat_list();
let mut goat_list = test_wrapper.pin_mut().get_goat_list(); // test_list는 UniquePtr<CxxVector<Goat>> 타입이다. 

println!("goat_list len : {}", test_list.len()) // 3 (마리)출력! 

for a in temp.pin_mut().iter_mut() {
    let horns = a.get_horns();
    println!("horns : {}", horns);
}

Rust에서 autocxx를 사용할 때, c++ 객체를 pin_mut()으로 고정(Pinning) 해야 하는 경우가 많다. 그 이유는 c++ 객체의 메모리 위치를 변경하지 않도록 보장하기 위해서다. Rust에서는 기본적으로 소유권 이동이 적용된다. 하지만 c++에서는 포인터를 사용해 객체를 직접 가르키는 경우가 많다. c++ 객체가 Rust에서 이동하게되면, c++ 코드가 유지하던 포인터 관계가 깨질 수 있기 때문에 이를 방지하기 위해서 객체를 고정해서 이동을 막는다.

또한 c++ 객체가 자기 자신을 참조하는 경우가 있을 수 있다.

class Test {
public:
    int value;
    Test* self;

    Test(int v) : value(v), self(this) {}

    void print_self() {
        std::cout << "My address: " << this << ", Stored address: " << self << std::endl;
    }
};

위와 같은 이유로 rust에서 c++ 객체를 사용시 pin_mut()를 해주어야 한다.

따라서 temp.pin_mut().iter_mut()가 중요하다.
iter_mut()Pin<&mut T>을 반환하는데 T타입은 Pin으로 고정된 상태여야한다.
std::vector<Goat> vector가 가지고 있는 Goat의 주소값을 고정시켜야 get_horns()를 호출할 수 있다.

만약 iter()를 사용하게 되면 에러가 발생한다.

결론

c++ 객체의 포인터 구조를 보장하기 위해서 rust에서 c++ 객체를 사용할 때 pin_mut()을 통해 객체의 메모리 위치를 변경하지 않도록 보장해야한다.

0개의 댓글