Что возвращается, когда функция Rust FFI возвращает структуру без # [repr (C)] для C? - PullRequest
0 голосов
/ 22 января 2019

РЕДАКТИРОВАТЬ: Было отмечено, что мой пример не достаточно полон, чтобы быть полезным. Мой вопрос решен, но если вам интересно посмотреть на полный код, вы можете увидеть его здесь .

С учетом следующего кода:

#[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 Ответ

0 голосов
/ 22 января 2019

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]

ЦСИ / 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

Пожалуйста, прочитайте Rust FFI Omnibus для получения подробной информации о том, как правильно передавать значения через границу FFI. Отказ от ответственности: я основной автор.

...