Hacker News new | past | comments | ask | show | jobs | submit login

> Use int-type enums with iota: […] no compile-time guard against illegal enum values

Create a new int type and use that for your enums. While you still can create an illegal enum value, you basically have to be looking for trouble. It’s not going to happen accidentally. It’s even harder if it’s an unpunished type in a different package.

See:

https://github.com/donatj/sqlread/blob/91b4f07370d12d697d18a...




This compiles:

  type Test int

  const (
    T1 Test = 0
    T2      = 1
  )

  func TestSomething(t Test) {}

  ...
  
  TestSomething(17)
So this isn't a good suggestion, because you can easily pass any int value and will not get a compiler error. You may as well be using strings at that point.


I generally tend to use enumer[0] to generate some boilerplate code that can help with addressing this, e.g. the below would compile, but would error at runtime. There are probably linters out there that could catch this. With Go, linters are generally pretty good at catching this kind of stuff.

    package main
    
    import "fmt"
    
    type Test int
    
    const (
     T1 Test = 0
     T2      = 1
    )
    
    func main() {
     t, err := TestString("T1")
     if err != nil {
      panic(err)
     }
    
     TestSomething(t)
    }
    
    func TestSomething(t Test) {
     fmt.Println(t.String())
    }
Having said that, it seems weird to have to mimic enums, as opposed to actually having it. Doesn't feel like it would add much complexity, if at all.

[0] https://github.com/dmarkham/enumer


> as opposed to actually having it

Like C or C++ do? :)


Right. However, C and C++ are far more complex, with no memory safety. Everything has it's ups and downs.


Your point is valid, but the Go philosophy depends on you following conventions to have reliable code. This is true all over the place, e.g. you can easily ignore errors.

Other languages take a stricter approach, and maybe that's better. Not defending (although I like Go), but it's really more a language philosophy than a singular defect.

As the other commenter noted, this should fail code review and you should be using the provided constants, and it should be clear to you. And if you disagree (which again is totally valid), you should use a stricter language—there's plenty out there!


Fwiw a literal 17 in a function call, let alone anywhere outside an equation or constant definition is a code smell that should never make it past review.

I see your point however.


Depending on code review instead of a static type system does not scale. Look at all of the memory safety security vulnerabilities that are solved by "simply making sure to manage memory correctly."

Also:

  variableDefinedInAFarAwayModule := 17

  ...

  TestSomething(variableDefinedInAFarAwayModule)
  
It's not always as clear as a constant value being passed to an incorrect type.


I don't understand your example here, that's not going to compile.

variableDefinedInAFarAwayModule is definitionally type int and will not be cast. It is also unpublished, so you couldn't be using it for a faraway module?

Your 17 in the previous example has it's typed determined at compile time which is why it can be a problem.

see: https://go.dev/play/p/jEdAhKDeLy6


Ah thank you. That's slightly better then.


This will not compile because the type of the variable is int not Test.


I mean, that's a pretty common usage of enums, isn't it?

    TestSomething(T1 & T2)


And this is the big, probably irreconcilable, difference in culture between the sum-typers and the compiler-assisted-named-valuers....


As amw-zero pointed out though, a user could accidentally create the int type and it would only fail if you have runtime checking (which requires you to build it, either via a custom enum constructor that returns `error` or an `IsValid` function, which then require you to maintain the ValidEnums list).

Int types also don't give you any guards when deserializing.


One caveat here is serialization. Writing your (or another package's) enum to a database will get you in trouble if you ever want to add another value in the middle. Sure, you can be careful and should document this, but who knows


Not only that, but the person sending you the serialized object might be looking for trouble. Sending you an enum value that is outside the legal range might help an attacker get into your system.




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

Search: