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)]

4. mod ffi {
5. use std::os::raw::{c_char, c_int, c_long, c_ulong, c_ushort};

7. // Opaque type. See https://doc.rust-lang.org/nomicon/ffi.html.
8. #[repr(C)]
9. pub struct DIR {
10. _data: [u8; 0],
11. _marker: core::marker::PhantomData<(*mut u8, core::marker::PhantomPinned)>,
12. }

14. // Layout as per readdir(3) and definitions in /usr/include/x86_64-linux-gnu.
15. #[repr(C)]
16. pub struct dirent {
17. pub d_ino: c_long,
18. pub d_off: c_ulong,
19. pub d_reclen: c_ushort,
20. pub d_type: c_char,
21. pub d_name: [c_char; 256],
22. }

24. extern "C" {
25. pub fn opendir(s: *const c_char) -> *mut DIR;
26. pub fn readdir(s: *mut DIR) -> *const dirent;
27. pub fn closedir(s: *mut DIR) -> c_int;
28. }
29. }

31. use std::ffi::{CStr, CString, OsStr, OsString};
32. use std::os::unix::ffi::OsStrExt;

34. #[derive(Debug)]
35. struct DirectoryIterator {
36. path: CString,
37. dir: *mut ffi::DIR,
38. }

40. impl DirectoryIterator {
41. fn new(path: &str) -> Result<DirectoryIterator, String> {
42. // Call opendir and return a Ok value if that worked,
43. // otherwise return Err with a message.
44. unimplemented!()
45. }
46. }

48. impl Iterator for DirectoryIterator {
49. type Item = OsString;
50. fn next(&mut self) -> Option<OsString> {
51. // Keep calling readdir until we get a NULL pointer back.
52. unimplemented!()
53. }
54. }

56. impl Drop for DirectoryIterator {
57. fn drop(&mut self) {
58. // Call closedir as needed.
59. unimplemented!()
60. }
61. }

63. fn main() -> Result<(), String> {
64. let iter = DirectoryIterator::new(".")?;
65. println!("files: {:#?}", iter.collect::<Vec<_>>());
66. Ok(())
67. }