امن بودن 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ها تبدیل خواهید کرد:
&str
toCString
: you need to allocate space for a trailing\0
character,CString
به*const i8
: برای فراخوانی توابع C به یک اشارهگر نیاز دارید,- از
*const i8
به&CStr
: به چیزی نیاز دارید که بتواند کاراکتر\0
را پیدا کند, &CStr
به&[u8]
: یک slice بایت universal interface برای «برخی دادههای ناشناخته» است،- از
&[u8]
به&OsStr
:&OsStr
گامی به سویOsString
است، ازOsStrExt
برای ایجاد آن استفاده کنید، &OsStr
toOsString
: you need to clone the data in&OsStr
to be able to return it and callreaddir
again.
مورد 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(()) }