Что возвращается, когда функция RFI FFI возвращает структуру без #[repr(C)] для C?
РЕДАКТИРОВАТЬ: было отмечено, что мой пример не является достаточно полным, чтобы быть полезным. Мой вопрос решен, но если вы заинтересованы в просмотре полного кода, вы можете увидеть его здесь.
Учитывая следующий код:
#[no_mangle]
pub extern "C" fn create_acceptor() -> Acceptor {
Acceptor {}
}
pub struct Acceptor {}
Если я вызываю это из C, что это на самом деле получается? Это указатель? Идентификатор какой-то?
В настоящее время я использую код, подобный следующему на стороне C:
extern int create_acceptor();
int main(int argc, char **argv) {
int acceptor = create_acceptor();
return 0;
}
Вроде нормально работает. Я использую его как непрозрачный тип, который я передаю обратно в Rust, как acceptor_doSomething(acceptor)
,
Но я запутался, потому что, кажется, не имеет значения, какой тип я использую для acceptor
на стороне C. void*
а также int
оба ведут себя одинаково. Кроме того, документация, кажется, указывает, что я должен использовать #[repr(C)]
, но, кажется, работает без него. Это почему?
При печати значения, полученного на стороне C, отображаются очень маленькие целые числа, поэтому я предполагаю, что это идентификатор на стороне Rust?
1 ответ
TL;DR: пример не полезен и ничего не показывает.
что это на самом деле получает? Это указатель? Идентификатор какой-то?
Это структура, точно такая, как определена на стороне Rust. Это указатель, если вы возвращаете указатель (в этом примере это не так) или идентификатор, если вы возвращаете какой-то идентификатор (в этом примере это не так).
FFI от Rust не накладывает никаких накладных расходов или абстракций - вы возвращаете то, что возвращаете.
документация указывает на то, что я должен использовать
#[repr(C)]
Да, ты должен. Без этого ваш код, скорее всего, будет неопределенным поведением. Расположение структуры Rust еще не гарантировано. Без указания представления как C, для кода на C невозможно узнать, где находятся поля.
но, кажется, работает без него
Это потому, что в вашем примере struct нет полей и, следовательно, нет размера (что AFAIK даже не допустимо в C без нестандартных расширений). Русту практически никогда не нужно смотреть на данные (какие данные?), Поэтому не имеет значения, что вы передаете обратно.
При печати значения, полученного на стороне C, отображаются очень маленькие целые числа
Это, вероятно, просто мусор в стеке, лежащем вокруг. Буквально неинициализированные данные.
Использование структуры с полями без #[repr(C)]
Это плохо Не делай этого. String
является структурой с нетривиальными полями, и она не помечена #[repr(C)]
,
Cargo.toml
[package]
name = "shear"
version = "0.1.0"
authors = ["An Devloper"]
[lib]
crate-type = ["cdylib"]
[dependencies]
SRC / lib.rs
// Don't do this, `String` isn't #[repr(C)]!
#[no_mangle]
pub extern "C" fn create_example() -> String {
String::from("hello")
}
// Don't do this, `String` isn't #[repr(C)]!
#[no_mangle]
pub extern "C" fn use_example(e: String) {
println!("{}", e);
}
main.c
extern int create_example();
extern void use_example(int example);
int main(int argc, char **argv) {
int example = create_example();
use_example(example);
}
выполнение
$ cargo build
Compiling shear v0.1.0 (file:///home/ubuntu/shear)
Finished dev [unoptimized + debuginfo] target(s) in 1.02s
$ gcc -o example main.c -L target/debug/ -lshear
$ LD_LIBRARY_PATH=target/debug/ ./example
Segmentation fault
Пожалуйста, прочитайте The Rust FFI Omnibus для получения подробной информации о том, как правильно передавать значения через границу FFI. Отказ от ответственности: я основной автор.