5 ms·
Unfortunately not. func main() { c := color.Red cptr := (*string)(&c) *cptr = "orange" PrintColor(c) // successfully compiles, and prin
by kiitos 3y ago
Unfortunately not.
func main() {
c := color.Red
cptr := (*string)(&c)
*cptr = "orange"
PrintColor(c) // successfully compiles, and prints "orange"
}
- revoly 3y agoI don't think the solution is trying to cover this. If a user does type conversion then usually they know what they are doing. It's like going out of the way. The solution is to just hide a type behind an interface to be explicit about it and to avoid the error that would have gone unnoticed otherwise.
- throwaway894345 3y agoThe type conversion is a red herring. He's just changing the value of a variable by referencing and dereferencing a pointer. In other words, he's not using a pointer conversion to change the value of `color.Red`, he's creating a new variable `c` and assigning `color.Red` to it, and then changing the value of the variable through the pointer and type conversion. But he doesn't even need to do the pointer/type conversion stuff, he could just do `c = Color("orange")` and call it a day.
- kiitos 3y ago> But he doesn't even need to do the pointer/type conversion stuff, he could just do `c = Color("orange")` and call it a day. c = Color("orange") does not compile.
- throwaway894345 3y agoAh, I misunderstood what type color.Red had. My mistake.
- kiitos 3y agoThe article is titled "Compile-time safety for enumerations in Go" but my example demonstrates that this is not the case.
- taeric 3y agoThe counter point is that this is like arguing that an article about safe knife skills are silly, because you can still just stab yourself?
- kiitos 3y agoI'm not sure how that's comparable. Compile-time type safety is a boolean thing, you either have it or you don't. If you have it, then my example shouldn't compile. But it does compile, so you don't have it.
- taeric 3y agoI think by insisting to view it only as a boolean thing is very limiting. By that measure, are the type safety hints of Python not really giving any type safety? Just give up and go home, as it were? :D I'm sympathetic to the points, I think. But I also don't have a problem with someone pushing for some type safety as long as you avoid actively trying to go against it.
- kiitos 3y ago> By that measure, are the type safety hints of Python not really giving any type safety? If we're talking compile-time, then: yes, absolutely! Type hinting doesn't provide type safety, because type safety isn't a spectrum.
- throwaway46281 3y agoTo me it's pretty clear that safety is a spectrum (e.g. we give cars safety ratings), and therefore so is type safety. Is "type safety" something you saw defined in a PL book or something, which has a more rigid definition to you?
- throwaway894345 3y agoYou'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)
- masklinn 3y ago> 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
- throwaway894345 3y agoYeah, I thought Color was `type Color string`. I misunderstood what he was doing.
- tedunangst 3y agoAt some point I think it's worth asking who are we trying to stop and why.
- kiitos 3y agoIndeed. Go types are like simple locks: they keep honest users honest, but they don't prevent dishonest users from breaking your assumptions. The point is just that "compile-time type safety for enumerations" isn't actually true, or even possible. You always have to do some amount of runtime validation of received values.
- chabad360 3y agoNo, you don't. If your module relies on compile time checks for validity, there's no reason for you to also include runtime checks. Other developers breaking your defined contract is not your responsibility, it's not even a bug.
- kiitos 3y ago> If your module relies on compile time checks for validity, ...then it's broken, because the Go compiler, factually, cannot (in general) check or enforce the validity of specific values. > there's no reason for you to also include runtime checks. Other developers breaking your defined contract is not your responsibility, it's not even a bug. It is absolutely the responsibility of the function func PrintColor(c Color) to ensure, at runtime, that `c` is a valid Color. My example code -- which allowed an invalid Color value of "orange" -- is absolutely a bug in PrintColor.
- ori_b 3y agoExactly. Someone could always connect to your program with a debugger and modify the instructions are being run, and that's why you need to regular runtime checks of the validity of your program text. If you want to handle people actively trying to undermine the assumptions your program is making, you can't leave out easy to replicate cases like 'gdb attach', obvious, non-accidental use of the type system, and other similar categories of issues. What strategies do you use in your programs to ensure type safety in the event of debugging, mmap, and other mechanisms that can be used to subvert the memory layouts your compiler thinks it produced?
- avg_dev 3y agoi appreciate this counter-example; thanks. i was hoping there was a way to get real enums in go.
- gabereiser 3y agoIn this example, *cptr is a color by contract. A Color is a type alias of a string. *cptr is a string. Interfaces match.
- kiitos 3y agoTechnically not a type alias, just a normal type declaration. But yes, this is my point: you can't assume that an enum type will always be one of a fixed set of values that you've defined. You always have to do at least a little bit of runtime validation of the parameter. It's unavoidable.
- gabereiser 3y agoalias and type redefinitions are conceptually the same. int and int_t are conceptually the same. I’m going to assume it’s my type, as defined in the signature of my method you’re calling. If you don’t adhere to the contract then the onus is on you. I’ll check existence, I’ll check conformity, but I won’t check that your string = my string.
- kiitos 3y ago> alias and type redefinitions are conceptually the same. In Go, they are not. Aliases establish precise equivalence between types, type redefinitions establish new types altogether.
- hamdouni 3y agoIt's the game of the mouse and the cat :-D We can change the package color like this : package color type Color interface { void() } type color struct { v string } func (c color) void() {} func (c color) String() string { return c.v } var ( Red color = color{"red"} Green color = color{"green"} Blue color = color{"blue"} )
- kiitos 3y agofunc fn(c color.Color) { switch c { case color.Red, color.Green, color.Blue: log.Printf("c=%#+v -- OK", c) default: log.Printf("c=%#+v -- should not be possible", c) } } func main() { var c color.Color fn(c) } Output: c=<nil> -- should not be possible :shrug: