Rust 구조체를 Python 클래스로 만드는 예제

Pt J·2026년 3월 20일
post-thumbnail

Rust 구조체를 Python 클래스로 만드는 예제

Rust의 구조체를 Python의 클래스와 연동하는 예제다.
많은 경우 우리는 데이터를 단일 변수가 아니라 연관된 덩어리로 관리하기 때문에
Rust로 작성한 로직을 Python에서 사용하고자 할 때 반드시 알아야 할 내용이다.

작업공간 생성 및 구조 확인

~/workspace$ mkdir struct-to-class && cd struct-to-class
~/workspace/struct-to-class$ python3 -m venv venv
~/workspace/struct-to-class$ source venv/bin/activate
~/workspace/struct-to-class$ pip install maturin fastapi uvicorn
~/workspace/struct-to-class$ maturin init
~/workspace/struct-to-class$ # 선택지 중 기본값인 PyO3 선택
~/workspace/struct-to-class$ # Cargo.toml과 src/lib.rs가 자동 생성된다
~/workspace/struct-to-class$ # Python 코드는 직접 생성해 주어야 한다
~/workspace/struct-to-class$ mkdir app && touch app/main.py
~/workspace/struct-to-class$ tree -I venv
.
├── app
│   └── main.py
├── Cargo.toml
├── pyproject.toml
└── src
    └── lib.rs

Cargo.toml 파일을 열어 라이브러리 이름을 수정해 주겠다.

Cargo.toml

[package]
name = "struct-to-class"
version = "0.1.0"
edition = "2024"

[lib]
name = "rust_engine"
crate-type = ["cdylib"]

[dependencies]
pyo3 = "0.28.0"

코드 작성

Rust 코드

Python에서 클래스로 사용하고자 하는 구조체에는 #[pyclass] 속성을 붙인다.
#[pyo3(get)] 속성이 붙은 값은 Python에서 별도의 함수 없이 직접 불러올 수 있으며
#[pyo3(set)] 속성이 붙은 값은 Python에서 별도의 함수 없이 직접 입력할 수 있다.

Python에서 클래스의 메서드로 사용하고자 하는 구조체 구현에는 #[pymethods] 속성을 붙인다.
#[new] 속성이 붙은 함수는 생성자로 사용되며 Python의 __init__ 역할을 한다.

구조체도 함수와 마찬가지로 #[pymodule] 속성이 붙은 함수에서 등록해야 사용할 수 있다.

src/lib.rs

use pyo3::prelude::*;

#[pyclass]
struct Counter {
    #[pyo3(get)]
    value: i64,
}

#[pymethods]
impl Counter {
    #[new]
    fn new(initial_value: i64) -> Self {
        Counter { value: initial_value }
    }

    // 이하 Python에서 호출할 메서드
  
    fn increment(&mut self, amount: i64) {
        self.value += amount;
    }

    fn get_value(&self) -> i64 {
        self.value
    }
}

#[pymodule]
fn rust_engine(m: &Bound<'_, PyModule>) -> PyResult<()> {
    m.add_class::<Counter>()?;

    Ok(())
}

Python 코드

Rust에서 구조체로 정의한 클래스의 인스턴스를 만들어 사용한다.

app/main.py

from fastapi import FastAPI
import rust_engine

app = FastAPI()

my_counter = rust_engine.Counter(100)

@app.get("/counter")
def read_counter():
    return {
        "current_value": my_counter.value
    }

@app.get("/counter/add/{amount}")
def add_to_counter(amount: int):
    my_counter.increment(amount)

    return {
        "added": amount,
        "new_value": my_counter.get_value(),
        "type_from_rust": str(type(my_counter))
    }

빌드 및 실행

Maturin 라이브러리를 통해 Rust 코드를 Python에서 호출 가능한 형태로 컴파일한다.
컴파일 후 pip list 명령어를 사용해 보면 Cargo.toml 파일에 작성한 패키지 이름을 확인할 수 있다.

uvicorn 라이브러리를 통해 FastAPI를 실행한다.

~/workspace/struct-to-class$ maturin develop
~/workspace/struct-to-class$ uvicorn app.main:app --reload

curl 명령어 또는 브라우저를 통해 다음과 같은 테스트를 해볼 수 있다.

  • http://127.0.0.1:8000/counter
  • http://127.0.0.1:8000/counter/add/42
  • http://127.0.0.1:8000/counter/add/-20
profile
Peter J Online Space - since July 2020 | 아무데서나 채용해줬으면 좋겠다

0개의 댓글