Official way to get the size of a field

I'm currently wrapping the posix readdir{,_r}() in the manner performed by the rust stdlib for fs::read_dir(), which delegates to readdir_r() by default except for a specified list of platforms: rust/library/std/src/sys/fs/unix.rs at aa95b9648ad0383a3fd73b5271dd86f848b7c00c · rust-lang/rust · GitHub. If I understand correctly, the stdlib is relying on the result of readdir_r() always fitting into the stack-allocated dirent struct initialized with mem::zeroed() in the linked method body. In particular, contrast to the fs::read_dir() implementation immediately above used for linux, which must allocate an owned CString upon each iteration: rust/library/std/src/sys/fs/unix.rs at aa95b9648ad0383a3fd73b5271dd86f848b7c00c · rust-lang/rust · GitHub.

The dirent struct used for these platforms has a d_name field which is a fixed-size array of c_char, which is why the rust stdlib is able to avoid allocating a new CString on these platforms without introducing unsafety. See macos (libc/src/unix/bsd/apple/mod.rs at c6a3cd61445c38160c07b507617bb6099602759a · rust-lang/libc · GitHub):

pub struct dirent {
    pub d_ino: u64,
    pub d_seekoff: u64,
    pub d_reclen: u16,
    pub d_namlen: u16,
    pub d_type: u8,
    pub d_name: [c_char; 1024],
}

I am looking to get that array length in const context so I can make a trait like:

trait HasMaxSizedName {
  const LEN: usize;
  fn name(&self) -> &[c_char; Self::LEN];
}

#[cfg(target_vendor = "apple")]
impl HasMaxSizedName for libc::dirent {
  // want # of array elements instead of byte length here 
  const LEN: usize = field_size!(Self, d_name);
  fn name(&self) -> &[c_char; Self::LEN] { &self.d_name } 
}

I would like to have this information available in the type system so I can use something like CStr::from_bytes_until_nul() on a bounded-size array, as opposed to using the unsafe method CStr::from_ptr() like the stdlib currently does (rust/library/std/src/sys/fs/unix.rs at aa95b9648ad0383a3fd73b5271dd86f848b7c00c · rust-lang/rust · GitHub).

For the stdlib's purposes, it actually could use CStr::from_bytes_until_nul(), since it's dealing with a concrete type. But I'm trying to make this HasMaxSizedName trait to differentiate platforms with a fixed-size immediate buffer from platforms like linux which must always eagerly allocate a new CString before moving to the next iteration result from fs::read_dir(). This is opposed to the stdlib's approach here, which forces all platforms into a single struct and uses unsafe methods for all of them, even though some of them could be safer. As of now, I am using lifetimes to propagate this ownership information, but being able to get the size of an array field in a type-level context like an associated constant would be much more ergonomic.

2 Likes