Live data from Hacker News

Compile-time safety for enumerations in Go

vladimir.varank.in

91–100 of 116 posts

Re: Compile-time safety for enumerations in Go

#91
post #2

I dunno if I'd consider having a dummy method on an interface as "elegant", but it does work. A trade-off of keeping the language very simple, for sure. What I really wish Go had was sum types, Rust style. That'd cover enumerations and more.

I hope Go will eventually get sum types but I hope they're better than Rust's where each variant is its own type.

Re: Compile-time safety for enumerations in Go

#92
post #89
post #88

Earlier quoted context omitted.

> Casting as demonstrated in the example is normal and has to be expected. Can you give an example where someone has done similar casts accidentally? It seems like it would be hard to accidentally typo. That leaves malice, which seems difficult to defend against, in light of the kinds of system calls that are available to a program.

Sure, by parsing a string to a Color value.

The example given wasn't a simple cast of a value, and definitely not an implicit coercion. The example is more like C-style type punning where you explicitly cast a pointer to the value and then write through the dereferenced pointer.

I don't doubt that there are niches where such explicit type coercion patterns are common in Go and susceptible to mistakes, but I doubt usage of constant identifiers is such a niche.

Rust is currently the standard bearer for strong, static type safety, and it even has both the enum types and pattern matching construct which Go lacks. AFAIU, you can use unsafe{} Rust code to perform a similar type punning trick, successfully assigning an invalid value to an enum object. I don't know if Rust's code generator always inserts runtime validity checks in match statements on an enum value without a catchall/default case, but certainly it's possible for Rust code to have an explicit if/else chain that at compile time appears comprehensive but which would neither panic nor produce the expected behavior. Does that mean Rust programmers shouldn't rely on Rust's static typing, instead always adding explicit code to handle unknown/invalid enum values?

Maybe the assertion that Go code should have such checks is more reasonable than for Rust code, but you haven't explained how. At least to me, the simplest, minimal code to achieve the subversion in both Go and Rust seems similarly stilted and similarly unlikely to be written by mistake. (To be clear, the context of this subthread as I understand it assumes the interface method hack, the subversion of which requires the type punning.)

Re: Compile-time safety for enumerations in Go

#93
post #32

This is not elegant. It also has overhead. Stick with the simple `const Red Color = "red"`, you're not gaining a lot by doing all these strange type contortions in Go. Seriously consider what you are protecting against, and whether it's an imagined bogeyman. "Oh but someone may try to cast arbitrary values to my package type" Okay but who is that going to hurt? You or them? Will they get hit with errors early on in t…

It's stuff that just creeps in. Enum values can come from outside (json, anything non-literal). Now you have to do validation, because the bad values parse and for sure exist at some stage in your program. It's not a bogeyman.

Yes it is important to have validation for anything coming in from outside (json, the database, etc). This is done by creating a map with the valid enum values and checking at runtime. This compile time solution wouldn't have worked for validating enums sent in json anyway, it's purely a defense mechanism against library users trying to cast arbitrary values to a package type.

Re: Compile-time safety for enumerations in Go

#94
post #2

I dunno if I'd consider having a dummy method on an interface as "elegant", but it does work. A trade-off of keeping the language very simple, for sure. What I really wish Go had was sum types, Rust style. That'd cover enumerations and more.

I hope Go will eventually get sum types but I hope they're better than Rust's where each variant is its own type.

How is this mess better than Rust's enums? A Rust enum + match statement results in really easy to read and clean code.

Re: Compile-time safety for enumerations in Go

#95
post #89
post #88

Earlier quoted context omitted.

> Casting as demonstrated in the example is normal and has to be expected. Can you give an example where someone has done similar casts accidentally? It seems like it would be hard to accidentally typo. That leaves malice, which seems difficult to defend against, in light of the kinds of system calls that are available to a program.

Sure, by parsing a string to a Color value.

Can you point at a git commit where someone had a similar bug implemented by accident and fixed it? The code you posted earlier seems to be something that would be incredibly hard to write without malice. And if you're considering malice, you also need to consider mmap, foreign function interfaces, writes to /dev/memory, and other similar perverse mechamisms.

Re: Compile-time safety for enumerations in Go

#96

Another approach could be: package color type Color struct { val string } func (c Color) String() string { return c.val } var ( Red = Color{val: "red"} Green = Color{val: "green"} Blue = Color{val: "blue"} ) Since `val` is not exported, external packages cannot create arbitrary `Color`.

Ins't it still possible to construct the zero value for Color ? Like how bytes.Buffer does it https://pkg.go.dev/bytes#Buffer : the implementation is not exported but you the usage is to construct the zero value and use it. So unless Color{} is a valid enum value, this solution would not work.

Re: Compile-time safety for enumerations in Go

#97
post #92
post #89

Earlier quoted context omitted.

Sure, by parsing a string to a Color value.

The example given wasn't a simple cast of a value, and definitely not an implicit coercion. The example is more like C-style type punning where you explicitly cast a pointer to the value and then write through the dereferenced pointer. I don't doubt that there are niches where such explicit type coercion patterns are common in Go and susceptible to mistakes, but I doubt usage of constant identifiers is such a niche.…

I don't know what point you're trying to make, beyond just objecting to the points that I'm trying to make. Not interested in continuing.

Re: Compile-time safety for enumerations in Go

#98

Earlier quoted context omitted.

You're not changing the underlying value of `color.Red`, you're changing the variable (in other words, `PrintColor(color.Red)` will still print `red`). If that's the point you're intending to make, then couldn't you make it more easily with this?: c := color.Red c = Color("orange") PrintColor(c)

> You're not changing the underlying value of `color.Red` Doesn't matter, the point is they're getting in a value which is not part of the defined set, demonstrating that this emulation is not actually typed-safe. > If that's the point you're intending to make, then couldn't you make it more easily with this?: cannot convert "orange" (untyped string constant) to type Color

Yeah, I thought Color was `type Color string`. I misunderstood what he was doing.

Re: Compile-time safety for enumerations in Go

#99
post #63

Go doesn’t have type-safe enums in the language? That’s wild, even C compilers have this feature now (optionally), and then there’s Rust/Swift’s capabilities.

Welcome to Go. “Wait, Go doesnt have X?” I’m convinced its an incomplete language masquerading as a simple one.

It is one of those languages that I will use if I have to, not because I want to.

Re: Compile-time safety for enumerations in Go

#100

Here is an issue tracking a possible fix to this: https://github.com/golang/go/issues/19412

This is a more recent proposal, and at this time more likely to get traction: https://github.com/golang/go/issues/57644

As stated in that proposal, the interaction between what people want from “enums,” “discriminated unions,” “sum types,” (as well as “optional values” and “nil safety”) and fundamental Go tenets like zero values, make this a tough sell, which is very likely to make no one happy.

Post reply on HN