Ответ 1
Пока еще нет возможности автоматически создавать определения типа Rust из C-структур. В этих ситуациях есть несколько способов для продолжения. Не зная API MySQL, я не могу точно сказать, что вы должны делать, но вот несколько вариантов.
1) Рассматривайте их целиком как непрозрачные указатели.
Это лучшая ситуация, в которой вы находитесь, и зависит от API-интерфейса C, который всегда принимает структуру как указатель, имеет свои собственные функции конструктора и деструктора и предоставляет функции доступа для всего, что вам нужно для доступа внутри структуры. В этих случаях вы просто определяете type MYSQL = ctypes::void
и только когда-либо используете его как небезопасный указатель *MYSQL
. Иногда самый простой путь - написать свои собственные C-обертки, чтобы заполнить пробелы и сделать возможным этот сценарий.
Остальные сценарии включают переопределение структуры данных Rust с той же структурой, что и структура C. Rust пытается выложить свои структуры данных таким образом, который совместим с C (хотя это не всегда удается), поэтому часто можно создать запись или перечисление Rust с размером, выравниванием и компоновкой структуры C, которую вы заботиться. Вы хотите, чтобы вы использовали типы в core::ctypes
, поскольку они определены для соответствия различным общим типам C.
Обратите внимание, что модуль ctypes
скоро исчезнет в пользу более полного модуля совместимости libc.
2) Определите неправильную запись Rust.
Если API предоставляет конструкторы и деструкторы, но вам по-прежнему нужен доступ к некоторым полям структуры, вы можете определить, достаточно ли структуры, чтобы добраться до полей, о которых вы заботитесь, не обращая внимания на такие вещи, как правильные размеры и выравнивание. например type MSQL = { filler1: ctypes::int, ..., connector_fd: *ctypes::char }
. Вы можете прекратить определение структуры в последнем поле, о котором вы заботитесь, поскольку у вас есть функция C, чтобы выделить ее в куче с правильным размером и выравниванием. В коде Rust вы всегда ссылаетесь на него с небезопасным указателем: let mysql: *MYSQL = mysqlrust::create_mysql();
3) Определите запись ржавчины, которая является правильным размером и выравниванием, не заботясь о содержимом.
Если у вас нет функций конструктора/деструктора или вам нужно сохранить структуру в стеке, но у вас есть другие функции доступа для управления содержимым структуры, тогда вам нужно определить запись ржавчины с правильным размером и выравнивание. Для этого просто добавьте поля типа uint
(который всегда имеет размер указателя) или кортежи uint
, пока оба C sizeof
и core::sys::size_of
не согласятся на размер. Pad с u8
, если размер не кратен размеру указателя. Получение правильности выравнивания является более мистическим процессом, но, используя поля uint
, вы, как правило, получаете удобное выравнивание (возможно, я действительно не знаю, насколько точным является этот оператор).
Я бы рекомендовал добавить тесты для проверки работоспособности, чтобы Rust и C договорились о размере, чтобы защитить от возможного повреждения.
3) На самом деле переопределите всю структуру C.
Это довольно тяжелая ситуация для больших структур, и это возможно в теории, но я не думаю, что кто-то сделал это для структуры размером с MYSQL
. Я бы избегал этого, если можно. В конце концов, для этого будет создан инструмент, основанный на clang.
Вот несколько примеров взаимодействия с C-структурами:
https://github.com/jdm/rust-socket/blob/master/socket.rs - Это переопределяет различные структуры сокетов, добавляя заполнители для полей, на которые это не заботит. Обратите внимание, что для заполнения используется u8
, но я думаю, что uint
с большей вероятностью создаст правильное выравнивание.
https://github.com/erickt/rust-zmq/blob/master/zmq.rs
https://github.com/pcwalton/rust-spidermonkey - Это демонстрирует взаимодействие с несколько сложным API.