Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve empty_enum lint #4905

Closed
Luro02 opened this issue Dec 15, 2019 · 4 comments · Fixed by #5087
Closed

Improve empty_enum lint #4905

Luro02 opened this issue Dec 15, 2019 · 4 comments · Fixed by #5087
Labels
A-documentation Area: Adding or improving documentation

Comments

@Luro02
Copy link

Luro02 commented Dec 15, 2019

This code

#![deny(clippy::empty_enum)]
use core::marker::PhantomData;

enum Marked {}
enum UnMarked {}

struct Data<T> {
    _p: PhantomData<T>,
}

fn main() {}

causes the following error:

error: enum with no variants
 --> src/main.rs:4:1
  |
4 | enum Marked {}
  | ^^^^^^^^^^^^^^
  |
note: lint level defined here
 --> src/main.rs:1:9
  |
1 | #![deny(clippy::empty_enum)]
  |         ^^^^^^^^^^^^^^^^^^
help: consider using the uninhabited type `!` or a wrapper around it
 --> src/main.rs:4:1
  |
4 | enum Marked {}
  | ^^^^^^^^^^^^^^
  = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#empty_enum

So it's telling me to replace my empty enums with ! (which doesn't make sense in this case) or make a wrapper around !.

The problem is, that there is nowhere mentioned how this wrapper type should look like and what the benefits are over an empty enum?!

Playground

@flip1995 flip1995 added the A-documentation Area: Adding or improving documentation label Dec 21, 2019
@flip1995
Copy link
Member

This help message should be improved along with the documentation. In the mean time you can read up on empty enums here: https://doc.rust-lang.org/nomicon/exotic-sizes.html?highlight=enum#empty-types

@flip1995
Copy link
Member

TL;DR: You should use ! wherever you use your empty enum as a type or create a wrapper struct Marked(!) (currently feature gated).

If you just want to mark something, you should use ZSTs instead of empty types. (struct Marked instead of enum Marked)

@Luro02
Copy link
Author

Luro02 commented Dec 22, 2019

@flip1995 Thanks for the link, I read through it and your TL;DR and I have some questions, but first I should clarify my current use for an empty enum:

I have a file parser for a format with several versions, and a newer version has a field, that doesn't exist on older ones, so I wrote my API like this

use core::marker::PhantomData;

enum V2 {}

struct FileData<T> {
    data: Option<String>,
    _p: PhantomData<T>,
}

impl FileData<V2> {
     pub fn data(&self) -> &String {
         &self.data.as_ref().unwrap()
     }
}

This makes it possible for the developer to specify at compile time, which format they use and therefore removes the need for pointless unwrapping.


With the above in mind, I ask myself why I should use

struct V2;

over

enum V2 {}

The benefit of an empty enum is, that it can't be constructed and therefore the compiler can optimize my code, but this would no longer be possible with struct V2, because it can be constructed and therefore optimizations like the one mentioned in the link (Result<T, V2> to T) would no longer be possible.

Another thing, that confuses me is that there are multiple ways to achieve the same thing:

struct Marked(!); // can this be constructed?
struct Marked;

enum Marked {};

type Marked = !; // is there still a difference between this and `type NotMarked = !;`? like there would be with `enum Marked {}` and `enum NotMarked {}`
// what about
type Marked = ();

@flip1995
Copy link
Member

I understood your first example like you wanted to just mark something and didn't care if it could be constructed.

If you really need something that should not be constructed, you have no other way than using an empty type/enum on stable, since ! is still unstable.

struct Marked(!); // can never be contructed, since ! is uninhabited (nightly only)

enum Marked {}; // can never be constructed, since it is an empty type

type Marked = !; // That is the same as using `!`, but with a name (only available on nightly). I'd suggest to use this method if you are on nightly and don't want something to be constructed

type Marked = (); // This is just a ZST, not what you want

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-documentation Area: Adding or improving documentation
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants