واجهات الوظائف الخارجية (FFI)
(الـ) iroha_ffi الصندوق يقدم الكليات والخصائص لإنتاج C ABI التزامات من Rust APIs. يتم استخدامه عند Iroha أنواع تحتاج إلى عبور FFI الحدود، على سبيل المثال من خلال SDK التزامات أو تكاملات المضيف.
لماذا FFI
الوظيفة هي كيان مجرد إلى حد ما، وعلى الرغم من أن معظم اللغات تتفق على ما يجب أن تفعله الوظيفة، فإن الطريقة التي يتم بها تمثيل الوظائف مختلفة جداً. وبالإضافة إلى ذلك، في بعض اللغات، مثل Rust ، فإن عواقب استدعاء وظيفة والأشياء التي يسمح لها بالقيام بها مختلفة أيضًا. عندما تحتاج إلى استدعاء Rust APIs من لغة أخرى أو بيئة مضيفة مختلفة، يستخدم Iroha واجهة وظيفة أجنبية (FFI) لتسوية الحالة.
المعيار الرئيسي المستخدم اليوم هو واجهة التطبيقات الثنائية C. إنها بسيطة ومتوفرة على نطاق واسع ومستقرة. في المبدأ ، يمكنك القيام بكل شيء يدوياً ، ولكن Iroha يوفر صندوق iroha_ffi لتوليد وظائف متوافقة مع FFI من Rust موجودة API.
يمكنك، بالطبع، القيام بذلك بطريقتك. الصندوق iroha_ffi يخلق فقط الشفرة التي ستحتاج إلى توليدها على أي حال. كتابة اللوحة المطلوبة تتطلب قدرًا كبيرًا من الكثافة والتربية. كل مكالمة وظيفة فوق حدود FFI هي unsafe مع إمكانية أن تسبب سلوك غير محدد. الطريقة التي تمكنا من حلها، تتداول حول استخدام صيغ قوية repr(C) .
INFO
الاستثناء الوحيد هو المؤشرات. لا يمكن فرض التحقق الصفري والصلاحية على المستوى العالمي، لذلك لا يتم استخدام المؤشرات الخام (كما هو الحال دائما) إلا في حالات استثنائية. نظرًا لأننا نقدم ملفات حول كل مثال تقريبًا من كائن في نموذج البيانات Iroha ، لا يجب عليك استخدام مؤشرات خامة على الإطلاق.
مثال
وهنا مثال على توليد الالتزام:
#[derive(FfiType)]
struct DaysSinceEquinox(u32);
#[ffi_export]
impl DaysSinceEquinox {
pub fn update_value(&mut self, a: &u8) {
self.0 = *a as u32;
}
}النموذج المذكور أعلاه سيخلق الارتباط التالي مع DaysSinceEquinox تمثله كمرجع غير شفاف:
pub extern fn DaysSinceEquinox__update_value(handle: *mut DaysSinceEquinox, a: *const u8) -> FfiReturn {
// function implementation
}FFI الجيل المرتبط
يستخدم صندوق iroha_ffi لتوليد الوظائف التي يمكن استدعاءها عبر FFI. بالنظر إلى هيكلات وطرق Rust ، فإنها تولد رمز unsafe الذي ستحتاج إليه لعبور الحدود المرتبطة.
يتم تحويل نوع Rust إلى نوع قوي repr(C) يمكنه عبور حدود FFI مع FfiType::into_ffi ، وهذا يسير بالعكس أيضًا: يتم تحويل النوع FFI ReprC إلى نوع Rust عن طريق FfiType::try_from_ffi.
WARNING
لاحظ أن التحويل المعاكس خاطئ ويمكن أن يسبب سلوك غير محدد. في حين أننا يمكن أن نبذل قصارى جهدنا لتجنب الأخطاء الأكثر وضوحا، يجب عليك التأكد من صحة البرنامج على وجهك.
السمات الرئيسية التي تمكن من توليد الالتزام هي ReprC، FfiType، و FfiConvert.
| سمة | وصف |
|---|---|
ReprC | هذه الصفة تمثل نوعًا قويًا يتوافق مع C ABI. يمكن مشاركة النوع بأمان عبر حدود FFI. |
FfiType | تحدد هذه السمة نوع ReprC المقابلة لنوع Rust معين. يتم استخدام النوع ReprC المحدد بدلاً من نوع Rust في وظيفة API التي تم إنشاؤها FFI . |
FfiConvert | هذه السمة تحدد طريقتين into_ffi و try_from_ffi تستخدم لتنفيذ التحويل من نوع Rust إلى أو من نوع ReprC. |
لاحظ أنه لا توجد نقل ملكية على FFI باستثناء أنواع المؤشرات غير الشفافة. جميع الأنواع الأخرى التي تحمل الملكية ، مثل Vec<T> ، يتم نسخها.
اسم (مانجلينغ)
لاحظ استخدام علامات تحتية مزدوجة في الأسماء التي تم إنشاؤها لأجسام FFI:
بالنسبة لنهج
inherent_fnالمحدد على هيكلStructName، فإن اسم FFI سيكونStructName__inherent_fn.بالنسبة إلى طريقة
MethodNameمن صفةTraitNameفي هيكلStructName، فإن اسم FFI سيكونStructNameTraitNameMethodName.لتحديد حقل
field_nameفي هيكلStructName، سيكون اسم الوظيفة FFI هوStructName__set_field_name.للحصول على حقل
field_nameفي هيكلStructName، سيكون اسم الوظيفة FFI هوStructName__field_name.للحصول على حقل
field_nameالمتغير في هيكلStructName، سيكون اسم الوظيفة FFI هوStrucuName__field_name_mut.بالنسبة لـ
module_name::fn_nameالمستقلة، فإن اسم FFI سيكونmodule_name::__fn_name.بالنسبة للخصائص التي ليست عامة وتسمح بمشاركة تنفيذها في FFI (انظر
Cloneأدناه) ، سيكون اسم FFImodule_name::__clone.rustimpl Clone for Type1 { fn clone(&self) -> Self; } impl Clone for Type2 { fn clone(&self) -> Self; }