امن بودن FFI Wrapper

زبان Rust پشتیبانی بسیار خوبی برای فراخوانی توابع از طریق رابط تابع خارجی foreign function interface (FFI) دارد. ما از آن برای ساختن یک پوشش امن برای توابع libc استفاده می‌کنیم که از C برای خواندن نام فایل ها در یک فهرست استفاده می کنید.

شما می‌خواهید به صفحات راهنما مراجعه کنید:

همچنین می‌خواهید std::ffi را مرور کنید

انواعرمزگذاریاستفاده
str و StringUTF-8پردازش متن در Rust
CStr and CStringNUL-terminatedارتباط با توابع C
OsStr و OsStringمخصوص سیستم‌عاملبرقراری ارتباط با سیستم‌عامل

شما بین تمام این typeها تبدیل خواهید کرد:

  • &str to CString: 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 to OsString: you need to clone the data in &OsStr to be able to return it and call readdir 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(())
}