How to match against an enum with conditional members?


#1

If I have:

pub enum FcntlArg<'a> {
    F_SETLKW(&'a flock),
    F_GETLK(&'a mut flock),
    #[cfg(any(target_os = "linux", target_os = "android"))]
    F_OFD_SETLK(&'a flock),
}

and then want to match:

let res = unsafe {
     match arg {
         F_SETLKW(flock) => ffi::fcntl(fd, ffi::F_SETLKW, flock),
         F_GETLK(flock) => ffi::fcntl(fd, ffi::F_GETLK, flock),
         _ => unimplemented!()
     }
};

How do I handle the conditional enum member? On other platforms, the ‘_’ match gets an unreachable pattern error, even.


#2

You should be able to add #[cfg]s to match arms too, e.g.

let res = unsafe {
     match arg {
         F_SETLKW(flock) => ffi::fcntl(fd, ffi::F_SETLKW, flock),
         F_GETLK(flock) => ffi::fcntl(fd, ffi::F_GETLK, flock),
         #[cfg(any(target_os = "linux", target_os = "android"))]
         F_OFD_SETLK(_lock) => unimplemented!(),
     }
};

#3

But that means that the #[cfg] has to be reproduced exactly wherever you want to match on it.

I would think that conditional enum variants are not good style.


#4

Do you have some thoughts about what are good alternatives? I agree that reproducing #[cfg]s in a lot of places can be annoying, but it seems like a relatively safe way to have conditional support for some functionality. (I can’t think of a scenario where accidentally mistyping a #[cfg] won’t hit a compilation error, but I guess there might be some.)


#5

I’d probably define all variants unconditionally. On unsupported OS, they just wouldn’t be used.

This might force people to add more _ => arms to their matches, but on the other hand it helps avoid gratuitous non-portability (package that doesn’t use the Unix-only feature at all still fails to compile on Windows because of nonexhaustive match).