How can a generic type exclude zero-sized types?

I'd like a struct to take a generic type T where std::mem::size_of::<T>() >= 1. I can use a const to simulate this, like so:

use std::mem;

pub struct NotZeroSized<T> {
    data: T
}
impl<T> NotZeroSized<T> {
    #[deny(const_err)]
    const _TEST_ZERO_SIZE_: usize = 0 - (mem::size_of::<T>() == 0) as usize;
    
    pub fn new(data: T) -> Self {
        // The next line must be in a function that is
        // called in order to trigger a const error.
        #[allow(path_statements)] { Self::_TEST_ZERO_SIZE_; }
        
        Self { data }
    }
}

But is there a less hacky way to do it?

In standard, idiomatic Rust, you have two options:

  • Handle the zero sized type case. Typically this can be done in a clever way that avoids putting undue strain on the common case; an if mem::size_of::<T>() == 0 will trivially optimize to one case or the other and not performing the actual check.
  • Forbid the use of ZSTs via an unsafe guard at the point of construction.

You need to be very careful if you're using lints, as --cap-lints exists and is used by cargo for dependencies, so you do not see lints that fire in dependencies.

2 Likes

Yeah, that's what I feared. I was trying to figure out how to make an unsafe interface safe, but it only worked if no ZSTs were used. So I'll probably give up trying.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.