Safe FFI Wrapper

Rust has great support for calling functions through a foreign function interface (FFI). We will use this to build a safe wrapper for the libc functions you would use from C to read the filenames of a directory.

You will want to consult the manual pages:

You will also want to browse the std::ffi module, particular for CStr and CString types which are used to hold NUL-terminated strings coming from C. The Nomicon also has a very useful chapter about FFI.

Copy the code below to https://play.rust-lang.org/ and fill in the missing functions and methods:

  1. // TODO: remove this when you're done with your implementation.
  2. #![allow(unused_imports, unused_variables, dead_code)]
  3. mod ffi {
  4. use std::os::raw::{c_char, c_int, c_long, c_ulong, c_ushort};
  5. // Opaque type. See https://doc.rust-lang.org/nomicon/ffi.html.
  6. #[repr(C)]
  7. pub struct DIR {
  8. _data: [u8; 0],
  9. _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
  10. }
  11. // Layout as per readdir(3) and definitions in /usr/include/x86_64-linux-gnu.
  12. #[repr(C)]
  13. pub struct dirent {
  14. pub d_ino: c_long,
  15. pub d_off: c_ulong,
  16. pub d_reclen: c_ushort,
  17. pub d_type: c_char,
  18. pub d_name: [c_char; 256],
  19. }
  20. extern "C" {
  21. pub fn opendir(s: *const c_char) -> *mut DIR;
  22. pub fn readdir(s: *mut DIR) -> *const dirent;
  23. pub fn closedir(s: *mut DIR) -> c_int;
  24. }
  25. }
  26. use std::ffi::{CStr, CString, OsStr, OsString};
  27. use std::os::unix::ffi::OsStrExt;
  28. #[derive(Debug)]
  29. struct DirectoryIterator {
  30. path: CString,
  31. dir: *mut ffi::DIR,
  32. }
  33. impl DirectoryIterator {
  34. fn new(path: &str) -> Result<DirectoryIterator, String> {
  35. // Call opendir and return a Ok value if that worked,
  36. // otherwise return Err with a message.
  37. unimplemented!()
  38. }
  39. }
  40. impl Iterator for DirectoryIterator {
  41. type Item = OsString;
  42. fn next(&mut self) -> Option<OsString> {
  43. // Keep calling readdir until we get a NULL pointer back.
  44. unimplemented!()
  45. }
  46. }
  47. impl Drop for DirectoryIterator {
  48. fn drop(&mut self) {
  49. // Call closedir as needed.
  50. unimplemented!()
  51. }
  52. }
  53. fn main() -> Result<(), String> {
  54. let iter = DirectoryIterator::new(".")?;
  55. println!("files: {:#?}", iter.collect::<Vec<_>>());
  56. Ok(())
  57. }