Можно ли получить указатель на функцию C в Rust и вызвать ее обратно? - PullRequest
1 голос
/ 08 мая 2020

Я изучаю взаимодействие Rust и C ++ (с использованием интерфейса C). Это пример из руководства Mozilla по Rust на Android:

use std::os::raw::{c_char};
use std::ffi::{CString, CStr};

#[no_mangle]
pub extern fn rust_greeting(to: *const c_char) -> *mut c_char {
    let c_str = unsafe { CStr::from_ptr(to) };
    let recipient = match c_str.to_str() {
        Err(_) => "there",
        Ok(string) => string,
    };

    CString::new("Hello ".to_owned() + recipient).unwrap().into_raw()
}

как вы можете видеть, ожидается указатель C char, и он также возвращает указатель C char.

Я хочу использовать код Rust в качестве библиотеки в моем проекте. Этот код Rust будет доставлять мне пакеты, когда они будут готовы. Поэтому мне нужен способ передать обратный вызов Rust, чтобы он мог вызвать его, когда у него есть данные. Примерно как этот фрагмент C ++:

extern "C" onData(uint8_t* payload, size_t size) {
    //do something with payload
}

MyRustType myRustObject("initialization_string");
myRustObject.setOnDataCallback(&onData);
myRustObject.beginWork();

Итак, «класс» ржавчины, MyRustType, начнет работать и доставлять пакеты через onData. Можно ли передать функцию C как указатель на Rust?

Если нет, я могу передать указатель как число uint64_t и заставить Rust передать это число + полезную нагрузку обратно в C функция, которая, в свою очередь, переводит этот указатель uint64_t в функцию и вызывает ее с полезной нагрузкой. Но я думаю, что, поскольку Rust настолько C дружелюбен, есть способ сделать это лучше.

1 Ответ

6 голосов
/ 08 мая 2020

Вы можете свободно, но небезопасно звонить из C в Rust и из Rust в C.

Из Rust в C: напишите блок extern "C" с прототипами C функций.

От C до Rust: напишите функцию Rust с префиксом #[no_mangle] extern "C". Вы должны убедиться, что любое полученное или возвращаемое значение является FFI-дружественным: базовые c типы или repr(C) типы или необработанные указатели на те и некоторые другие.

Также вам потребуются объявления, эквивалентные C объявлениям Rust. для этого вы можете использовать типы в std::os::raw, std::ffi и внешний ящик libc.

В частности, ответ на ваши вопросы об указателях функций, например, в C:

typedef int (*callback)(uint8_t *data, size_t len);

переводится на Rust как:

type Callback = unsafe extern "C" fn(data: *mut u8, len: usize) -> c_int;

Этот тип Callback совместим с FFI, поэтому вы можете хранить его, передавать между Rust и C и называть его когда угодно. И, конечно, вы можете использовать его в качестве аргумента или типа возвращаемого значения в других объявлениях FFI:

int do_the_thing(uint8_t*data, size_t len, callback cb);
#[no_mangle]
pub extern "C" fn do_the_thing(
    data: *const u8, len: usize,
    cb: Option<Callback>) -> c_int)
{
    //...
}

Помните, что в Rust функции указателя не допускают значения NULL, поэтому, если вы хотите, чтобы C мог чтобы передать NULL, вы используете Option<fn>.

Подробнее о FFI в Rust вы можете прочитать в этой главе Rustonomicon .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...