Использование () (и других типов нулевого размера) в FFI - PullRequest
2 голосов
/ 17 февраля 2020

Что эквивалентно () (или любому другому типу нулевого размера ) при выполнении FFI в Rust. В частности, мне любопытно, какой самый разумный эквивалент () в качестве аргумента функции при написании функции extern "C".

Насколько я понимаю, типы нулевого размера недопустимы в C, но Кажется, что Rust позволяет их использовать в extern "C" функциях, например:

#[no_mangle]
pub extern "C" fn test_ffi(input: ()) -> () {
}

В этом случае возврат () аналогичен объявлению функции void в C#. Однако мне не ясно, как вы объявите аргумент input при генерации привязки из C. У меня сложилось впечатление, что ZST не могут быть представлены в C, и поэтому не должны быть FFI-безопасными. Кажется, что nomicon подтверждает это , говоря:

И чтобы избежать предупреждения об использовании () в FFI, вместо этого мы используем пустой массив ([u8; 0]), который работает так же хорошо, как пустой тип, но является FFI-совместимым.

Что означает, что () не является FFI-совместимым, но это [u8; 0] (хотя я бы ожидать, что это также будет нулевым размером)?

Ответы [ 2 ]

5 голосов
/ 17 февраля 2020

Нет эквивалента () в C. Некоторые могут утверждать, что void эквивалентно, но на самом деле это не так. Конечно, они имеют сходство, в большинстве случаев они взаимозаменяемы, как вы сказали: pub extern "C" fn test_ffi() -> () будет правильно интерпретироваться как void test_ffi(void). (Примечание: здесь void внутри списка параметров означает, что функция не принимает аргументов, поэтому она не является пустым типом).

Вы можете думать о void как ничего , но это () ничего? Нет, это пустой кортеж, в то время как void на самом деле просто ничто.

У меня сложилось впечатление, что ZST не могут быть представлены в C и поэтому не должны быть FFI-безопасными. .

Нет, они не представимы в C: pub extern "C" fn test_ffi(input: (), foo: i32) -> () здесь неясно, что должен понимать компилятор Rust, потому что void test_ffi(void, int32_t foo); недопустим в C.

Nomicon использует пустой массив, чтобы сделать типы непрозрачными. Я не рекомендовал бы это, но это могло бы быть хорошо для этого определенного c варианта использования. Непрозрачные типы в C в любом случае являются злом. Обратите внимание, что пустые массивы недопустимы в C, поэтому их следует использовать только на стороне Rust.

Я бы посоветовал никогда не использовать типы нулевого размера в любой FFI.

1 голос
/ 17 февраля 2020

Void имеет перегруженное значение в C / C ++. Это может означать, что функция не принимает аргументов или ничего не возвращает, или это может быть void*, указатель на некоторые данные, но не говоря, что это за данные.

В первом случае для функций, которые ничего не возвращают или не принимать аргументов, просто опустите их в описании вашей функции. Функция, которая ничего не возвращает, технически возвращает (), но ее не нужно явно писать. тип устройства служит в этом случае так же, как и void, даже если в качестве единицы измерения что-то вместо ничего .

Для последнего В случае void*, ящик winapi-rs определяет c_void как пустое перечисление, а затем использует mut *c_void или const *c_void в качестве типа аргументов и структур, которые используют пустой указатель.

...