Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

Enums are closed sets and trait objects are open sets. They are conceptually related concepts, but the language puts syntactic distance between the two, and I don't think it should.

There are lots of open design questions for every feature you propose, but all of them have been discussed and have higher or lower chance of making it into the language.



That's kind of greek to me, but shouldn't promising the compiler that my set is closed unlock more features instead of taking features away?


I'm not sure I understand your point, but I'll elaborate on ways that we could "homogenize" the two features:

---

We could add implicit enums to impl Trait, so that you could return different types from a function:

    fn foo() -> enum impl Display {
        if rand() > 0.5 {
            "str"
        } else {
            42
        }
    }
which would let you get around the problem of returning a type erased object for a Trait that isn't object safe:

    trait Trait {
        const C: i32 = 0;
    }
    impl Trait for i32 {}
    impl Trait for &'static str {}
    fn foo() -> Box<dyn Trait> {
        if true {
            Box::new("")
        } else {
            Box::new(42)
        }
    }

    error[E0038]: the trait `Trait` cannot be made into an object
     --> f500.rs:6:17
      |
    6 | fn foo() -> Box<dyn Trait> {
      |                 ^^^^^^^^^ `Trait` cannot be made into an object
      |
    note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
     --> f500.rs:2:11
      |
    1 | trait Trait {
      |       ----- this trait cannot be made into an object...
    2 |     const C: i32 = 0;
      |           ^ ...because it contains this associated `const`
      = help: consider moving `C` to another trait
      = help: the following types implement the trait, consider defining an enum where each variant holds one of these types, implementing `Trait` for this new enum and using it instead:
                &'static str
                i32
---

Relax object safety rules, like making all assoc consts implicitly `where Self: Sized`.

---

We could make enum variants types on their own right, allowing you to write

    fn foo() -> Result<i32, i32>::Ok { Ok(42) }

    let Ok(val) = foo();
There's some work on this, under the umbrella of "patterns in types". For now the only supported part of it is specifying a value range for integers, but will likely grow to support arbitrary patterns.

---

Having a way to express `impl Trait for Enum {}` when every `Enum` variant already implement `Trait` without having to write the whole `impl`.

---

Anonymous enums:

    fn foo() -> Foo | Bar | Baz
---

Being able to match on Box<dyn Any> or anonymous enums

    match foo() {
        x: Foo => ...,
        x: Bar => ...,
        _ => ...,
    }
---

Stop needing to create a new struct type in order to box a single variant

    enum Foo {
        Bar(Box<struct { a: i32, b: i32 }>),
    }
---

These are of the top of my head, there are many things that you can do to make trait objects and enums feel closer than they do today, to make changing the way your code works a "gradient" instead of a "jump". My go-to example for this is: if you have a type where every field is Debug, you can derive it. As soon as you add one field that isn't Debug, you have to implement the whole impl for your type. That's a "jump". If we had default values for structs you could still use the derive by specifying a default value in the definition. That makes the syntactic change "distance" be as far as the conceptual change "distance".


A trait is a collection of variants that may or may not have unknown members. An enum is a collection of variants that may not have unknown implementations. So enums are in some sense a subset of traits. Hence every property of traits is also a property of enums. Does that make sense?

Those suggestion of your look interesting, but I haven't thought them through enough to have an opinion.


> They are conceptually related concepts, but the language puts syntactic distance between the two, and I don't think it should.

Haskell's type classes are a bit like Rust's traits. Type classes are open by default, but optionally can be closed off.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: