Криптографічні операції, парсинг протоколів або обробка великих обсягів даних у мобільному додатку часто впираються в продуктивність нативного коду. Rust-код, інтегрований через Foreign Function Interface (FFI), вирішує ці задачі завдяки високій швидкості та строгій безпеці пам'яті. Наші інженери вже реалізували інтеграцію Rust в iOS та Android для фінтех-проектів і месенджерів: від синхронізації стану до реалізації протоколу Double Ratchet. Discord використовує Rust у мобільному клієнті, Signal — для libsignal. Rust забезпечує продуктивність, порівнянну з C++, але з гарантованою безпекою пам'яті на рівні компілятора — це критично важливо для захисту користувацьких даних. За даними Microsoft Security Development Lifecycle, близько 70% вразливостей у системному ПЗ пов'язані з проблемами пам'яті; Rust усуває цей клас помилок, знижуючи витрати на налагодження на 50-70%.
Архітектура інтеграції Rust через FFI
Rust компілюється в статичну бібліотеку (.a на iOS, .a/.so на Android) з C ABI через extern "C". Для генерації біндингів на стороні мобільного додатку ми використовуємо uniffi-rs (автоматично) або cbindgen (ручний контроль).
Cargo.toml:
[lib] crate-type = ["staticlib", "cdylib"] staticlib — для iOS (статичне лінкування), cdylib — для Android (динамічна .so). Cross-компіляція через cargo-ndk (Android) та стандартний Cargo з iOS targets.
Приклад збірки для iOS та Android
Targets для Android:
cargo ndk -t arm64-v8a -t x86_64 -o ./jniLibs build --release Targets для iOS:
cargo build --target aarch64-apple-ios --release cargo build --target aarch64-apple-ios-sim --release cargo build --target x86_64-apple-ios --release lipo -create target/x86_64-apple-ios/release/libmylib.a \ target/aarch64-apple-ios/release/libmylib.a \ -output libmylib-sim.a Потім xcodebuild -create-xcframework об'єднує device та simulator варіанти.
Чому варто обрати Rust замість C/C++ для нативної логіки?
Rust на порядок безпечніший за пам'яттю: компілятор гарантує відсутність use-after-free та станів гонок на етапі збірки. Для криптографії та парсингу протоколів це критично — помилка може коштувати даних користувачів. За даними Microsoft, близько 70% всіх вразливостей у системному ПЗ пов'язані з проблемами пам'яті. Rust усуває цей клас помилок, що робить його в 2–3 рази безпечнішим за C/C++ за кількістю вразливостей, допущених розробниками. При цьому Rust не поступається C++ за продуктивністю, а для криптографічних операцій Rust-код працює в 3-5 разів швидше за еквівалентний Java-код на Android. Rust-рішення споживає в 2 рази менше пам'яті порівняно з C++ при тих же алгоритмах. Економія бюджету на розробку сягає 40% за рахунок повторного використання одного Rust-коду на обох платформах.
Порівняння інструментів: uniffi-rs vs cbindgen
| Інструмент | Автоматизація | Контроль | Підтримка async |
|---|---|---|---|
| mozilla/uniffi-rs | Висока (з UDL) | Середній | Експериментальна (0.25+) |
| cbindgen | Низька (тільки C-заголовки) | Повний | Вимагає ручної реалізації |
uniffi-rs — рекомендуємо для більшості проектів. Rust-інтерфейс описується у .udl файлі, uniffi-bindgen генерує Kotlin-класи для Android та Swift-файли для iOS. Результат — нативний API без ручного написання JNI або Objective-C.
// mylib.udl namespace mylib { sequence<u8> encrypt(sequence<u8> data, string key); }; Генерує mylib.kt з fun encrypt(data: List<UByte>, key: String): List<UByte> та mylib.swift з func encrypt(data: [UInt8], key: String) -> [UInt8].
cbindgen — генерує C-заголовний файл з Rust. Підходить якщо потрібен тонкий C-шар, а біндинги до Swift/Kotlin пишуться вручну або через інший інструмент. Більше контролю, більше ручної роботи.
Як забезпечити потокобезпечність при передачі даних через FFI?
На межі Rust↔мобільний потрібно явно керувати часом життя об'єктів. Якщо Rust-функція повертає вказівник на heap-аллоковану структуру — мобільний код отримує Long (Android) або UnsafeRawPointer (iOS). Знищення об'єкта — через явний free_object(ptr) на Rust-стороні, викликаний з finalize()/deinit. Забути викликати free_* — memory leak. Uniffi автоматизує це через Arc reference counting.
Rust паніка (panic!) через FFI — undefined behavior. Весь FFI-код обгортаємо в std::panic::catch_unwind або використовуємо #[no_panic] анотації для критичних шляхів.
Async Rust в FFI. tokio runtime можна створити всередині Rust-коду: Runtime::new().unwrap().block_on(async { ... }). Це синхронний виклик з точки зору FFI, але асинхронний всередині Rust. Для справжнього async взаємодії — callback-based API або uniffi-rs з підтримкою async (експериментально в uniffi 0.25+).
Кейс. Месенджер з end-to-end шифруванням: криптографічне ядро на Rust (Double Ratchet алгоритм, X3DH key exchange) через uniffi. Android: Kotlin викликає RatchetSession.encrypt(plaintext) — під капотом FFI до Rust. iOS: Swift викликає RatchetSession.encrypt(plaintext:). Один Rust-код — ідентична логіка на обох платформах. Unit-тести — на Rust (cargo test), інтеграційні тести — на Kotlin та Swift. CI: GitHub Actions, матриця з 4 target'ів, збірка xcframework та .aar як артефакти. 80% часу розробки пішло на налагодження нативного коду — Rust скоротив цей показник вдвічі.
Процес роботи
- Аналіз вимог — визначаємо, який код вигідніше винести в Rust, і оцінюємо потенційну економію ресурсів (приблизно в 2–3 рази знижуються витрати на налагодження за рахунок гарантій безпеки).
- Проектування FFI-шару — обираємо uniffi або cbindgen, проектуємо інтерфейс.
- Написання Rust-коду — реалізація алгоритмів з тестами (
cargo test). - Генерація біндингів та збірка — CI під обидві платформи, матриця target'ів.
- Інтеграція та тестування — unit-тести на мобільній стороні, профілювання.
Типові помилки на старті:
- Забути обробити Rust-паніку через
catch_unwind— undefined behavior на FFI-межі. - Не звільнити пам'ять вручну при використанні cbindgen — витік.
- Ігнорувати символи для налагодження — стектрейси без імен функцій.
Налагодження та профілювання
Rust-код у мобільному додатку налагоджується складніше нативного: LLDB підключається до процесу, символи завантажуються з .dSYM (iOS) або .so з debug info (Android). cargo build без --release зберігає debug symbols. Firebase Crashlytics показує стек до Rust-фрейму якщо символізація налаштована.
AddressSanitizer для Rust через RUSTFLAGS="-Z sanitizer=address" — знаходить use-after-free та buffer overflows у нативному коді до потрапляння в продакшен.
Що входить у роботу
- Аналіз вимог та проектування FFI-інтерфейсу
- Написання Rust-коду з unit-тестами (100% покриття критичних шляхів)
- Генерація біндингів через uniffi або cbindgen
- Налаштування CI/CD з матрицею target'ів (Android arm64, x86_64; iOS device, simulator)
- Інтеграція в мобільний додаток (Kotlin/Swift)
- Документація українською/англійською
- Навчання команди замовника (2 воркшопи)
- Підтримка протягом 3 місяців після здачі
Зв'яжіться з нами для оцінки вашого проекту — це займе не більше 2 днів. Наші інженери з 7+ роками досвіду в мобільній розробці реалізували понад 40 проектів з Rust-інтеграцією під ключ. Замовте консультацію, і ми підберемо оптимальне рішення для вашого стеку.
Строки
| Тип інтеграції | Орієнтовні строки |
|---|---|
| Проста функція через cbindgen (один алгоритм) | 2–3 тижні |
| Бібліотека зі станом через uniffi | 4–8 тижнів |
| Повноцінне криптографічне ядро | 2–5 місяців |
Вартість розраховується індивідуально. Ключові фактори: складність Rust API, вимоги до продуктивності, необхідність підтримки обох платформ. Оцінимо ваш проект за 2 дні — просто напишіть нам.







