امن بودن FFI Wrapper
زبان Rust پشتیبانی بسیار خوبی برای فراخوانی توابع از طریق رابط تابع خارجی foreign function interface (FFI) دارد. ما از آن برای ساختن یک پوشش امن برای توابع libc استفاده میکنیم که از C برای خواندن نام فایل ها در یک فهرست استفاده می کنید.
شما میخواهید به صفحات راهنما مراجعه کنید:
همچنین میخواهید std::ffi را مرور کنید
| انواع | رمزگذاری | استفاده |
|---|---|---|
str و String | UTF-8 | پردازش متن در Rust |
CStr and CString | NUL-terminated | ارتباط با توابع C |
OsStr و OsString | مخصوص سیستمعامل | برقراری ارتباط با سیستمعامل |
شما بین تمام این typeها تبدیل خواهید کرد:
&strtoCString: you need to allocate space for a trailing\0character,CStringبه*const i8: برای فراخوانی توابع C به یک اشارهگر نیاز دارید,- از
*const i8به&CStr: به چیزی نیاز دارید که بتواند کاراکتر\0را پیدا کند, &CStrبه&[u8]: یک slice بایت universal interface برای «برخی دادههای ناشناخته» است،- از
&[u8]به&OsStr:&OsStrگامی به سویOsStringاست، ازOsStrExtبرای ایجاد آن استفاده کنید، &OsStrtoOsString: you need to clone the data in&OsStrto be able to return it and callreaddiragain.
مورد Nomicon همچنین یک فصل بسیار مفید در مورد FFI دارد.
کد زیر را در https://play.rust-lang.org/ کپی کنید و توابع و متدهای از مفقود شده را پر کنید:
// TODO: این را زمانی که پیادهسازیتان تمام شد حذف کنید. #![allow(unused_imports, unused_variables, dead_code)] mod ffi { use std::os::raw::{c_char, c_int}; #[cfg(not(target_os = "macos"))] use std::os::raw::{c_long, c_uchar, c_ulong, c_ushort}; // Opaque type. See https://doc.rust-lang.org/nomicon/ffi.html. #[repr(C)] pub struct DIR { _data: [u8; 0], _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>, } // Layout according to the Linux man page for readdir(3), where ino_t and // off_t are resolved according to the definitions in // /usr/include/x86_64-linux-gnu/{sys/types.h, bits/typesizes.h}. #[cfg(not(target_os = "macos"))] #[repr(C)] pub struct dirent { pub d_ino: c_ulong, pub d_off: c_long, pub d_reclen: c_ushort, pub d_type: c_uchar, pub d_name: [c_char; 256], } // Layout according to the macOS man page for dir(5). #[cfg(all(target_os = "macos"))] #[repr(C)] pub struct dirent { pub d_fileno: u64, pub d_seekoff: u64, pub d_reclen: u16, pub d_namlen: u16, pub d_type: u8, pub d_name: [c_char; 1024], } extern "C" { pub fn opendir(s: *const c_char) -> *mut DIR; #[cfg(not(all(target_os = "macos", target_arch = "x86_64")))] pub fn readdir(s: *mut DIR) -> *const dirent; // See https://github.com/rust-lang/libc/issues/414 and the section on // _DARWIN_FEATURE_64_BIT_INODE in the macOS man page for stat(2). // // "Platforms that existed before these updates were available" refers // to macOS (as opposed to iOS / wearOS / etc.) on Intel and PowerPC. #[cfg(all(target_os = "macos", target_arch = "x86_64"))] #[link_name = "readdir$INODE64"] pub fn readdir(s: *mut DIR) -> *const dirent; pub fn closedir(s: *mut DIR) -> c_int; } } use std::ffi::{CStr, CString, OsStr, OsString}; use std::os::unix::ffi::OsStrExt; #[derive(Debug)] struct DirectoryIterator { path: CString, dir: *mut ffi::DIR, } impl DirectoryIterator { fn new(path: &str) -> Result<DirectoryIterator, String> { // Call opendir and return a Ok value if that worked, // otherwise return Err with a message. unimplemented!() } } impl Iterator for DirectoryIterator { type Item = OsString; fn next(&mut self) -> Option<OsString> { // Keep calling readdir until we get a NULL pointer back. unimplemented!() } } impl Drop for DirectoryIterator { fn drop(&mut self) { // Call closedir as needed. unimplemented!() } } fn main() -> Result<(), String> { let iter = DirectoryIterator::new(".")?; println!("files: {:#?}", iter.collect::<Vec<_>>()); Ok(()) }