https://velog.velcdn.com/images/prettylee620/post/592e8c19-a69e-4213-adcf-4617691dfb58/image.jpg

러스트 프로그래밍 언어에서 외부 함수 인터페이스(FFI, Foreign Function Interface)를 활용하여러스트 코드에서 C로 작성된 함수와 일부 C++로 작성된 함수를 호출할 수 있다.

러스트는 시스템 프로그래밍 언어로서 메모리 안전성을 강조하지만, 기존의 C/C++로 작성된 라이브러리를 활용하거나 시스템 호출을 직접 다루어야 하는 경우가 많다. 이때 FFI는 러스트 코드가 외부 함수와 데이터를 교환할 수 있게 해주는 중요한 메커니즘이다.

1. FFI(외부 함수 인터페이스) 개요

외부 함수 인터페이스(FFI)두 가지 이상의 프로그래밍 언어가 서로의 코드를 호출할 수 있도록 하는 메커니즘이다. 이는 주로 시스템 프로그래밍, 운영 체제 API 호출, 네트워크 통신, 하드웨어 제어, 고성능 연산이 필요한 분야에서 유용하다. 러스트는 다른 언어와의 상호작용을 위한 안전하고 효율적인 도구를 제공하며, FFI를 통해 기존 C/C++ 라이브러리와 쉽게 통합할 수 있다.

러스트는 메모리 안전성과 소유권 모델을 통해 안전한 코드 작성을 장려한다. 그러나 FFI를 사용할 때는 이러한 안전성이 완전히 보장되지 않는다. 외부 언어와의 상호작용에서 데이터 타입과 메모리 레이아웃의 호환성을 맞추는 것이 매우 중요하다. 이를 소홀히 하면 메모리 손상, 충돌, 예기치 않은 동작이 발생할 수 있다.

2. 러스트와 C 데이터 타입의 호환성

러스트와 C는 서로 다른 메모리 표현 방식과 데이터 정렬 방식을 사용하기 때문에, 두 언어 간의 데이터 타입을 호환시키는 것이 필요하다. 이를 위해 러스트에서는 libc 크레이트를 사용하여 C의 기본 데이터 타입과 호환되는 타입을 제공한다. 예를 들어, C의 int 타입은 러스트에서 c_int로, char 타입은 c_char로 매핑된다.

extern crate libc;

use libc::{c_int, c_char};

#[repr(C)]
struct MyStruct {
    field1: c_int,
    field2: c_char,
}

여기서 #[repr(C)] 어트리뷰트는 구조체의 메모리 레이아웃을 C와 동일하게 맞추도록 한다. 이 어트리뷰트를 사용하지 않으면 러스트 컴파일러는 구조체의 메모리 레이아웃을 최적화할 수 있으며, 이는 C 코드와의 호환성 문제를 일으킬 수 있다. 예를 들어, 멀티바이트 데이터가 포함된 구조체의 경우 데이터 정렬 방식에 따라 구조체의 크기와 메모리 배치가 달라질 수 있다. 따라서 #[repr(C)]는 FFI에서 매우 중요하다.

이 문서는 러스트와 C 언어 사이의 공통 데이터 표현에 대해 다룬다. 러스트와 C는 시스템 프로그래밍 언어로서, 기계 수준에서 데이터 표현 방식이 유사한 경우가 많다. 이러한 공통점 덕분에 러스트 프로그램이 C 라이브러리와 상호작용할 수 있다. 그러나 두 언어 간의 데이터 타입, 메모리 레이아웃, 문자열 처리 방식 등이 다르기 때문에, 상호 운용성을 보장하려면 특정 규칙을 따라야 합니다. 본 문서는 이를 해결하기 위한 기법들을 설명한다.

1) 러스트와 C의 공통 데이터 표현

러스트와 C는 시스템 프로그래밍 언어로서 기계 수준에서 데이터가 메모리에 어떻게 배치되는지에 대한 유사점을 공유한다.

예를 들어, 러스트의 usize와 C의 size_t는 동일한 데이터 표현을 가진다. 또한, 두 언어 모두 구조체를 지원하며, 구조체의 필드가 메모리에 순서대로 배치된다.

러스트의 데이터 타입은 C와의 호환성을 위해 std::os::raw 모듈을 통해 매핑된다. 이 모듈에는 C와 동일한 표현을 보장하는 여러 타입이 정의되어 있다.

기본적으로 제공되는 C 타입과 대응하는 러스트 타입의 매핑 표

C 타입 러스트 std::os::raw 타입
short c_short
int c_int
long c_long
long long c_longlong
unsigned short c_ushort
unsigned/unsigned int c_uint
unsigned long c_ulong
unsigned long long c_ulonglong
char c_char
signed char c_schar
unsigned char c_uchar
float c_float
double c_double
void *, const void * *mut c_void, *const c_void

즉, 이 설명은 std::os::raw 모듈에 정의된 타입들이 실제로는 러스트의 기본 타입에 대한 별칭(alias)이라는 뜻이다. 즉, 이들 타입은 새로운 타입이 아니라, 기존의 러스트 기본 타입에 이름만 다른 별칭을 붙인 것에 불과하다.

예를 들어, std::os::raw::c_char는 보통 i8 타입에 해당한다. 이는 c_char라는 이름을 통해 C 언어의 char 타입을 표현하는 것이며, 러스트에서는 실제로 i8 타입으로 구현된다는 의미이다. 따라서, C 언어와의 호환성을 위해 c_char라고 부르지만, 실제로는 러스트의 i8 타입을 사용하게 된다.