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

Add support for enum variants scoped under their type (enum mod) #10090

Closed
Kimundi opened this issue Oct 26, 2013 · 14 comments
Closed

Add support for enum variants scoped under their type (enum mod) #10090

Kimundi opened this issue Oct 26, 2013 · 14 comments

Comments

@Kimundi
Copy link
Member

Kimundi commented Oct 26, 2013

Current situation

Right now, enum types and their variants are scoped at the same level, which means this:

mod foo {
    enum Speed {
        Fast,
        Slow
    }
}

leads to these paths:

foo::Speed;
foo::Fast;
foo::Slow;

We will likely not change this, because it often makes working with enums easier, and because many languages with similar enum types work that way.

The problem

However, the restriction to a flat hierarchy currently leads to a lot of variants getting their type name embedded in the variant name to avoid collisions and/or ambiguity. For example, a random enum picked out of libsyntax reads like this:

pub enum Sigil {
    BorrowedSigil,
    OwnedSigil,
    ManagedSigil
}

The possible solution

To improve this situation, it would be convenient to have a enum declaration syntax for scoping the variants under their type instead, like this:

mod foo {
    enum mod Speed {
        Fast,
        Slow
    }
}
foo::Speed;
foo::Speed::Fast;
foo::Speed::Slow;

The above example could then get rewritten to

pub enum mod Sigil {
    Borrowed,
    Owned,
    Managed
}

and get used as Sigil::Borrowed, Sigil::Owned etc, or directly imported in the local scope if there is no collision.

Emulating it

You can currently kinda emulate it with

mod foo {
    pub type Speed = self::Speed::Speed;
    pub mod Speed {
        pub enum Speed {
            Fast,
            Slow
        }
    }
}

but this is very verbose, complicated, and creates more definitions and moving parts than necessary.

Working example

This uses the workaround above to show how using such a scoped enum would look like in practice.

#[feature(globs)];

use foo::Speed;

mod foo {
    pub type Speed = self::Speed::Speed;
    pub mod Speed {
        pub enum Speed {
            Fast,
            Slow
        }
    }
}

fn main() {
    let a: Speed = Speed::Fast;
    let b: Speed = Speed::Slow;

    match a {
        Speed::Fast => {}
        Speed::Slow => {}
    }

    {
        use foo::Speed::*;
        match b {
            Fast => {}
            Slow => {}
        }
    }
}
@sfackler
Copy link
Member

I'm a big fan of this. It'd be nice if the enum type name wasn't required in a match statement over a variable of the type, i.e.

pub enum mod Speed {
    Fast,
    Slow
}

let x = Speed::Fast;

match x {
    Fast => {}
    Slow => {}
}

As opposed to

match x {
    Speed::Fast => {}
    Speed::Slow => {}
}

@Kimundi
Copy link
Member Author

Kimundi commented Oct 27, 2013

@sfackler: I updated the Issue text with an example showing how that would be doable with the existing scoping primitives.

@auroranockert
Copy link
Contributor

I really like this idea.

@lilyball
Copy link
Contributor

lilyball commented Dec 2, 2013

I quite like this. I assume that the final code example in "The possible solution" was meant to say pub enum mod Sigil instead of pub enum Sigil?

@Kimundi
Copy link
Member Author

Kimundi commented Dec 2, 2013

@kballard Indeed, I forgot the mod there. 😃

@SimonSapin
Copy link
Contributor

Yes please. Servo's style system has tons of deeply nested modules that would become unnecessary with enum mod

@brendanzab
Copy link
Member

+1 here. Both styles - ie. enum and enum mod have their place. It would be good if the variants of an enum mod could be imported separately though. ie. use foo::Speed::Fast;

@sfackler
Copy link
Member

sfackler commented Dec 4, 2013

If we allow use foo::Speed::Fast, which seems pretty reasonable to me, what is the use case for normal enums? It might be too extreme of a change, but we could just change the semantics of enum to those of enum mod. Heavily used variants like Some would still be imported in std::prelude. I've personally always thought the way enum worked in C, C++, Rust etc was kind of weird.

@nikomatsakis
Copy link
Contributor

So I was thinking "can't this just be a macro?" and the answer is "not quite" -- in particular, the type of the enum would have to be Speed::t or something like that, since Speed is taken as the name of the module and modules/types inhabit the same namespace.

@SimonSapin
Copy link
Contributor

@nikomatsakis said:

in particular, the type of the enum would have to be Speed::t or something like that, since Speed is taken as the name of the module and modules/types inhabit the same namespace.

Do they really? The working example in the initial message seems to use Speed both as a module and a type.

@sfackler
Copy link
Member

sfackler commented Dec 4, 2013

@SimonSapin Something like this will fail:

pub use Thing::Thing;

pub mod Thing {
    pub enum Thing {}
}

pub fn do_a_thing(thing: Thing) {}
src/enum-mod/lib.rs:28:25: 28:30 error: found module name used as a type: mod Thing (id=15)
src/enum-mod/lib.rs:28 pub fn do_a_thing(thing: Thing) {}

@nikomatsakis My biggest problem with a macro-based solution is that it makes generated documentation enormously confusing. If I see a foo::Thing being used as an enum somewhere, and look at the documentation for foo, I would expect to see Thing in the "Enums" section of the documentation, but instead it's actually two layers of indirection away, hidden in the middle of a bunch of reexports and modules.

For example, here's rustdoc that uses a normal enum: http://sfackler.com/doc/enum/ and here's rustdoc for the same setup, but using the current workaround enum mod solution: http://sfackler.com/doc/enum-mod/

@sfackler
Copy link
Member

sfackler commented Dec 4, 2013

Oops, I was using a reexport above instead of a type alias. The http://sfackler.com/doc/enum-mod/ example is updated.

@alexcrichton
Copy link
Member

The consensus on this at today's meeting is that this feature should not be added to the language now. We may want to revisit this later (post-1.0), but for now we have decided to leave this out of 1.0

@SimonSapin
Copy link
Contributor

Look what just happened! #18973

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants