使用 C 的函式庫,簡單分兩個層次:
- 呼叫 C 的函式,以及提供 callback
- 使用 C 的資料結構。
這假日我好好研究了這個問題,驚奇發現 Rust 是僅次於 C++,我用過最容易和 C 界接的語言。以下以我最近處理的一個 struct 裡面有 struct,還有 enum,更有陣列的資料結構的簡化版,說明如何做出有同樣 layout 的 Rust 資料結構。
#define EC_MAX_STRING_LENGTH 64
#define EC_MAX_PORTS 4
typedef enum {
EC_PORT_NOT_IMPLEMENTED,
EC_PORT_NOT_CONFIGURED,
EC_PORT_EBUS,
EC_PORT_MII
} ec_slave_port_desc_t;
typedef struct {
uint8_t link_up;
uint8_t loop_closed;
uint8_t signal_detected;
} ec_slave_port_link_t;
typedef struct {
uint16_t position;
uint32_t vendor_id;
uint32_t product_code;
struct {
ec_slave_port_desc_t desc;
ec_slave_port_link_t link;
uint32_t receive_time;
uint16_t next_slave;
} ports[EC_MAX_PORTS];
uint8_t error_flag;
char name[EC_MAX_STRING_LENGTH];
} ec_slave_info_t;
以下使用 Rust 做出在記憶體 layout 一模一樣的資料結構:
// 首先要使用 libc 中提供的 C 的型別。
// int 對應 c_int
// uint 對應 c_uint
// char 對應 u8
// int16_t, uint16_t 等直接對應 int16_t 及 uint16_t。
extern crate libc;
use libc::{c_uint, c_int, int16_t, uint8_t, uint16_t, uint32_t};
const EC_MAX_PORTS: usize = 4;
const EC_MAX_STRING_LENGTH: usize = 64;
// 在 enum 前加上 #[repr(C)],告知 Rust 採用 C 的 layout。
#[repr(C)]
pub enum ec_slave_port_desc_t {
EC_PORT_NOT_IMPLEMENTED = 0,
EC_PORT_NOT_CONFIGURED,
EC_PORT_EBUS,
EC_PORT_MII,
}
#[repr(C)]
pub struct ec_slave_port_link_t {
link_up: uint8_t,
loop_closed: uint8_t,
signal_detected: uint8_t,
}
// 對於 C 結構中的陣列,就使用 Rust 的陣列取代。
#[repr(C)]
pub struct ec_slave_info_t {
pub position: uint16_t,
pub vendor_id: uint32_t,
pub product_code: uint32_t,
ports: [ec_slave_port_t; EC_MAX_PORTS],
al_state: uint8_t,
error_flag: uint8_t,
name: [u8;EC_MAX_STRING_LENGTH],
}
我產生上列程式的組合語言列表,確定它和 C 語言的記憶體配置一模一樣。 比較麻煩的是給預設值初始化時,這部份以後有機會再說明。