-
Notifications
You must be signed in to change notification settings - Fork 1.6k
new type of enum #3623
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
Comments
Anonymous enums/structs are a separate feature (discussed many times before) and should be tracked separately. Automatic So, other than somewhat shorter syntax, what are this syntax's benefits? Considering that Rust is already a complex language, and this doesn't add much new power/commonly requested feature, it seems better to save our complexity budget for other features. |
is this just #294 but with a name? the same sort of unresolved problem then type enum OneOf<E1, E2> {
E1, E2
}
fn which<E1, E2>(a: OneOf<E1, E2>) -> usize {
match a {
E1 => 1,
E2 => 2,
}
}
let f = OneOf::<i32, i32>::from(1_i32); // ?
let w = which(f); // ?
assert_eq!(w, 1); // ???? let g = OneOf::<&i32, String>::from("foo".to_string());
match &g { // can it interact with match ergonomics?
&str => println!("string {}", g),
i32 => println!("int {}", g),
} |
let f = OneOf::<i32, i32>::from(1_i32); // ?
let w = which(f); // ?
assert_eq!(w, 1); // ???? I guess I didn't make it clear, but obviously you cannot create I also do not propose the addition of Consider this: enum OneOf<E1, E2> { E1(E1), E2(E2) }
fn add_and_divide(a: i32, b: i32) -> Result<i32, OneOf<IntegerOverflow, DivByZero>> { .. }
fn main() {
let res = add_and_divide(1, 2);
match res {
Ok(num) => println!("{num}"),
Err(error) => match error {
E1(overflow) => println!("Error: Overflow happened: {overflow:?}"),
E2(div_by_zero) => println!("Error: Division by zero: {div_by_zero:?}"),
},
}
}
type enum OneOf<E1, E2> { E1, E2 }
fn add_and_divide(a: i32, b: i32) -> Result<i32, OneOf<IntegerOverflow, DivByZero>> { .. }
fn main() {
let res = add_and_divide(1, 2);
match res {
Ok(num) => println!("{num}"),
Err(error) => match error {
IntegerOverflow => println!("Error: Overflow happened: {error:?}"),
DivByZero => println!("Error: Division by zero: {error:?}"),
},
}
} Now we can change function type enum AddAndDivideError { IntegerOverflow, DivByZero }
fn add_and_divide(a: i32, b: i32) -> Result<i32, AddAndDivideError> { .. }
fn main() {
let res = add_and_divide(1, 2);
match res {
Ok(num) => println!("{num}"),
Err(error) => match error {
IntegerOverflow => println!("Error: Overflow happened: {error:?}"),
DivByZero => println!("Error: Division by zero: {error:?}"),
},
}
} And code in main functions doesn't need to change. We're getting better error handling with all errors being explicit and necessary to handle, so you can think about errors on higher level without the need of thinking about what enum they're actually represented with. I also don't think this is niche use case, because enums play a huge role in Rust ecosystem, and often each variant has associated data of different types (primarily being wrapper structs), which is great use case for this. I also don't think this adds much complexity, it may even simplify a lot of things, but I understand that those are primarily syntax changes. |
What is the meaning of "same type"? Are
No one mentioned
Sure it is not a niche use case given the number of duplicates of #294, the problem is how it fits into the type system. The main complexity here is the type-match expression. |
Am I missing something, or can the proposed syntax already be implemented through attribute macros? |
I think the meaning is types indistinguishable in match. For example currently you cannot use named lifetimes in patterns so types fn do_sm<'a, 'b>() {
let n = 10;
let ref1 = Refs::R1(&n);
match ref1 {
Refs::R1(&'a n) => {},
Refs::R2(&'b n) => {},
}
}
enum Refs<'a, 'b> {
R1(&'a i32),
R2(&'b i32),
}
Yes, enum syntax can be implemented using macros, but important part is how you would match on this type of enum using types directly. This is what you wouldn't be able to achieve without using macros around match, but this approach misses the idea of things being easy to use. |
Also I've read through previous RFCs with similar things suggested and I see the problem with type system ergonomics in match. I don't think we can cover every usage because how complex type system is, but I wonder if it is possible to add an exception with this one and just say variants in this enum have to be owned types with no lifetimes attached. If this isn't possible, I think all answers were already given in previous RFCs and this issue can be closed. |
No it's just because |
Add new type of enum to simplify handling enums with existing types as its variants.
Example:
This will dramatically simplify things like this:
Instead of currently available:
This will also make possible of more algebraic data types, such as:
Instead of currently possible:
Types like this work perfectly with
Result
:Instead of currently possible:
Or:
Additional things to consider:
Named variants in match:
Imports
Because type enum uses types themselves in match, we have to have a reliable way to access them.
Solutions might be:
Different ways to create
let value = NumOrString::from(10_i32);
let value: NumOrString = 10_i32;
let value = 10_i32 as NumOrString;
Anonymous structs if user wants to create new type associated with this type enum:
struct Handshake;
struct Message1;
struct Message2;
[1] This is currently impossible to have enum as part of enum:
This doesn't compile - we have to use wrapper structs for enums
The text was updated successfully, but these errors were encountered: