Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Безпечна обгортка інтерфейсу зовнішньої функції (FFI)

Rust має чудову підтримку виклику функцій через інтерфейс зовнішніх функцій (FFI). Ми скористаємося цим, щоб створити безпечну оболонку для функцій libc, які ви використовуєте у C для читання імен файлів у директорії.

Ви захочете переглянути сторінки посібника:

Ви також захочете переглянути модуль std::ffi. Там ви знайдете ряд типів рядків, які вам знадобляться для вправи:

ТипиКодуванняВикористання
str і StringUTF-8Обробка тексту в Rust
CStr і CStringNUL-термінованийСпілкування з функціями C
OsStr і OsStringСпецифічні для ОССпілкування з ОС

Ви будете конвертувати між усіма цими типами:

  • &str до CString: вам потрібно виділити місце для кінцевого символу \0,
  • CString до *const i8: вам потрібен покажчик для виклику функцій C,
  • *const i8 до &CStr: вам потрібно щось, що може знайти кінцевий символ \0,
  • &CStr до &[u8]: зріз байт є універсальним інтерфейсом для “деяких невідомих даних”,
  • &[u8] до &OsStr: &OsStr є кроком до OsString, використовуйте OsStrExt, щоб створити його,
  • &OsStr до OsString: вам потрібно клонувати дані в &OsStr, щоб мати можливість повернути їх і знову викликати readdir.

У 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};

    //Прозорий тип. Дивіться 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)>,
    }

    // Розміщення відповідно до man-сторінки Linux для readdir(3), де ino_t та
    // off_t розгорнуто відповідно до визначень у
    // /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],
    }

    // Розміщення відповідно до man-сторінки macOS для 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],
    }

    unsafe extern "C" {
        pub unsafe fn opendir(s: *const c_char) -> *mut DIR;

        #[cfg(not(all(target_os = "macos", target_arch = "x86_64")))]
        pub unsafe fn readdir(s: *mut DIR) -> *const dirent;

        // Дивіться https://github.com/rust-lang/libc/issues/414 та розділ про
        // _DARWIN_FEATURE_64_BIT_INODE у man-сторінці macOS про stat(2).
        //
        // "Платформи, які існували до виходу цих оновлень" відносяться
        // до macOS (на відміну від iOS / wearOS / тощо) на Intel і PowerPC.
        #[cfg(all(target_os = "macos", target_arch = "x86_64"))]
        #[link_name = "readdir$INODE64"]
        pub unsafe fn readdir(s: *mut DIR) -> *const dirent;

        pub unsafe 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> {
        // Викликати opendir і повернути значення Ok, якщо це спрацювало,
        // інакше повернути Err з повідомленням.
        unimplemented!()
    }
}

impl Iterator for DirectoryIterator {
    type Item = OsString;
    fn next(&mut self) -> Option<OsString> {
        // Продовжуємо викликати readdir до тих пір, поки не отримаємо назад вказівник NULL.
        unimplemented!()
    }
}

impl Drop for DirectoryIterator {
    fn drop(&mut self) {
        // Викликати closedir за необхідністю.
        unimplemented!()
    }
}

fn main() -> Result<(), String> {
    let iter = DirectoryIterator::new(".")?;
    println!("файли: {:#?}", iter.collect::<Vec<_>>());
    Ok(())
}