Интерфейс внешних функций (FFI)
Научитесь вызывать библиотеки C из Rust и предоставлять функции Rust другим языкам с помощью FFI для обеспечения совместимости.
«Интерфейс внешних функций (FFI)» — бесплатный урок Learn Rust Coding на CoddyKit. Это урок 1 из 3. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Learn Rust Coding, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Learn Rust Coding содержит 3 уроков всего.
Что такое FFI
FFI расшифровывается как интерфейс внешних функций. Это способ, позволяющий программам, написанным на одном языке программирования, вызывать функции или использовать структуры данных, определённые на другом языке.
В Rust FFI позволяет взаимодействовать с кодом, написанным на других языках, чаще всего на C. Это особенно полезно для интеграции с существующими библиотеками или системными интерфейсами программирования.
Зачем нужен FFI
FFI не является повседневным инструментом, но он незаменим, когда Вам требуется:
- Использовать существующие библиотеки C: Многие высокопроизводительные зрелые библиотеки написаны на C/C++. FFI позволяет Rust напрямую использовать их.
- Системные интерфейсы программирования: Взаимодействовать с возможностями операционной системы, для которых могут существовать только интерфейсы C.
- Совместимость: Создать библиотеку Rust, которую можно вызывать из других языков (например, Python, Java или JavaScript через Node.js).
Вызов C из Rust: объявление
Чтобы вызвать функцию C из Rust, сначала нужно объявить её сигнатуру с помощью блока extern "C". Это сообщает Rust, как выглядит функция C, не предоставляя её реализацию.
Часть "C" задаёт соглашение о вызовах C, обеспечивая совместимость вызовов функций.
extern "C" {
// Declare C functions here
// Example: int puts(const char *s);
fn puts(s: *const std::os::raw::c_char) -> std::os::raw::c_int;
// Example: int add(int a, int b);
fn add(a: std::os::raw::c_int, b: std::os::raw::c_int) -> std::os::raw::c_int;
}Вызов C из Rust: блок `unsafe`
Главное обещание Rust — безопасность памяти. Однако при взаимодействии с кодом C через FFI Rust не может гарантировать безопасность чужого кода.
Поэтому любой вызов функции C, объявленной в блоке extern "C", необходимо заключать в блок unsafe. Это сигнализирует компилятору и другим разработчикам, что именно Вы отвечаете за соблюдение гарантий безопасности.
extern "C" {
// We're declaring that a C function `puts` exists.
fn puts(s: *const std::os::raw::c_char) -> std::os::raw::c_int;
}
fn main() {
let rust_string = "Hello from Rust via C!";
// C strings need to be null-terminated.
let c_string = std::ffi::CString::new(rust_string).expect("CString failed");
unsafe {
// Calling the C 'puts' function. This requires 'unsafe'.
puts(c_string.as_ptr());
}
println!("C function call demonstrated.");
}Сопоставление типов: Rust и C
При использовании FFI необходимо убедиться, что типы Rust корректно соответствуют типам C. Для этого Rust предоставляет специальные типы в std::os::raw и std::ffi.
i32->std::os::raw::c_intf32->std::os::raw::c_floatbool->std::os::raw::c_uchar(или аналогичный тип, поскольку в C нет стандартного логического типа)&str->*const std::os::raw::c_char(строки C должны завершаться нулевым байтом; обычно это обеспечивается с помощьюstd::ffi::CString)&mut [T]->*mut T
Всегда используйте эти явные типы C, чтобы избежать неопределённого поведения.
Пример: вызов функции C `add`
Представим, что у нас есть библиотека C с функцией int add(int a, int b);. Вот как вызвать её из Rust. Обратите внимание на использование c_int для целочисленных типов.
(Этот код демонстрирует синтаксис FFI, но не запустится без скомпилированной библиотеки C, подключённой извне.)
extern "C" {
// Declaration of the C function `add`
fn add(a: std::os::raw::c_int, b: std::os::raw::c_int) -> std::os::raw::c_int;
}
fn main() {
let x = 10;
let y = 20;
let result = unsafe {
// Call the C 'add' function within an unsafe block
add(x as std::os::raw::c_int, y as std::os::raw::c_int)
};
println!("Attempted to call C add. Result (if linked): {}", result);
}Предоставление Rust для C: основы
Чтобы функцию Rust можно было вызывать из C, нужны две вещи:
#[no_mangle]: этот атрибут не позволяет компилятору Rust изменять имя функции («искажать» его). Компиляторы C ожидают простые имена без искажений.extern "C": это указывает, что функция должна использовать соглашение о вызовах C, благодаря чему она становится совместимой с C.
Пример: функция Rust для C
Вот функция Rust, которая вычисляет сумму и может вызываться из программы на C. Обратите внимание на атрибуты #[no_mangle] и extern "C".
(Этот код предназначен для компиляции в библиотеку для C, но содержит main для внутреннего тестирования в этой среде.)
#[no_mangle]
pub extern "C" fn rust_add_and_print(
a: std::os::raw::c_int,
b: std::os::raw::c_int
) -> std::os::raw::c_int {
let sum = a + b;
println!("Rust function received: {} + {} = {}", a, b, sum);
sum
}
fn main() {
println!("This Rust code exposes a function for C.");
// We can also call it internally for demonstration
let result = rust_add_and_print(5, 7);
println!("Internal call result: {}", result);
}Управление памятью в FFI
Управление памятью на границах FFI имеет критическое значение. Если Rust выделяет память (например, для строки) и передаёт указатель в C, C должен либо скопировать данные, либо корректно освободить выделенную память.
И наоборот, если C выделяет память и передаёт указатель в Rust, Rust не должен освобождать её без явного указания; в противном случае необходимо скопировать данные.
Такие средства, как std::ffi::CString и CStr, помогают безопасно работать со строками, совместимыми с C: они обрабатывают завершающий нулевой байт и обеспечивают корректную передачу владения.
Быстрая проверка FFI
Давайте проверим, насколько хорошо Вы поняли интерфейс взаимодействия Rust с внешними функциями.
FFI: мост между мирами
Вы изучили интерфейс взаимодействия Rust с внешними функциями!
FFI превращает Rust в мощный инструмент совместимости и позволяет Вам:
- вызывать функции из библиотек C с помощью блоков
extern "C"иunsafe; - предоставлять функции Rust другим языкам с помощью
#[no_mangle]иpub extern "C"; - тщательно сопоставлять типы с помощью типов
std::os::raw.
FFI — мощный инструмент для интеграции Rust с различными экосистемами, но помните о важности осторожного использования, особенно при работе с безопасностью и управлением памятью!
Часто задаваемые вопросы
Урок «Интерфейс внешних функций (FFI)» бесплатный?
Да — полный текст урока «Интерфейс внешних функций (FFI)» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Learn Rust Coding, подпишись на CoddyKit PRO. Курс Learn Rust Coding содержит 3 уроков всего.
Чему я научусь в уроке «Интерфейс внешних функций (FFI)»?
Научитесь вызывать библиотеки C из Rust и предоставлять функции Rust другим языкам с помощью FFI для обеспечения совместимости. Ты практикуешь Learn Rust Coding с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать Learn Rust Coding?
Предыдущий опыт не требуется. Learn Rust Coding на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 1 из 3.
Сколько времени занимает урок «Интерфейс внешних функций (FFI)»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке Learn Rust Coding?
Да. Каждый урок Learn Rust Coding включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Интерфейс внешних функций (FFI)
- Rust и WebAssembly (WASM)
- Бенчмаркинг и настройка производительности