Probably a good linter idea in there somewhere.
However, having made the mistake a few times now of trying to use "unsigned ints" for things that I want to assert are never negative, I've learned the hard way not to do that. The problem is, there's always some bug you have in the program that will drive the uint "negative", wrapping over to a huge number. Unfortunately, since you get no warning when that happens, it tends to take longer to show up, and without a clear cutoff to say "ah, this is clearly invalid" it can be hard to even program detection code, whereas checking for "less than 0" can be unambiguous.
I'd loooove it if I could easily, cheaply, and universally (i.e., not just in Go, but across all my programming languages) turn on a behavior that says "if this int or uint under or overflows, throw an exception instead of trying to do whatever stupid thing you're going to do". I get the sense this is heading down the road where in 20 or 30 years, this is just going to be common sense. However, we are very early on the road yet from what I can see. Whenever I bring it up, even today, even in this sort of context, it still is generally negatively received. (But not uniformly. A few other people agree. I think momentum is with me. I'm not sure my programming career will live long enough to see it, though.)
Overflow flags are supported on some architectures, so it would really just be a matter of checking that flag.
> For signed integers, the operations +, -, , /, and << may legally overflow and the resulting value exists and is deterministically defined by …*
It does not go on to define the overflow values; that gets to be implementation defined.
This is in contrast to unsigned integers, which the spec does precisely define overflow for.
If x is unsigned, then you can’t rearrange an inequality like x >= 4 into x - 4 >= 0, since the first inequality is only true sometimes, whereas the second is true always. This looks obvious, but bury it a little way into some nontrivial index arithmetic and boom.
Another more aesthetic reason is that indices can represent offsets into a structure relative to some other index, and in this case you really need negative numbers.
What would be a more compelling argument is to use int in the same way that python does - a negative accessor means offset from the end of the array, not the start. I'm not sure golang lets you do this though...?
I want to emphasize that I do not know the true answer to this question.
I think that indexes are int to play nicely with a decrementing loop counter. Doing:
for i := len(n) - 1; i >= 0; i-- { fmt.Println(n[i]) }
requires i to be signed due to the last iteration. for(size_t i = zn; i-- > 0 ;) printf("%s\n",n[i]);
(This is a large part of why unsigned over/underflow is well-defined behavior in C.)More generally, assignment is always a statement, and never an expression (e.g. you can't say a = b = c)
A modern language with this type of confusing and platform dependent behavior is inexcusable. By all means, include a native data type as big as the processor can handle (size_t), but don't call it int which is the default data type most people will use out of pure laziness.
Defaulting to 64 bit math on 32 bit systems will have a huge performance penalty on generally the slowest/oldest systems where this penalty is least desirable. It's not clear to me what the benefit to this would be for most programs.
Running 32 bit ints on 64 bit systems will cause problems handling large arrays on systems with a lot of memory. I think it also complicates generating efficient loop code, although C++ gets around this with signed integer overflow being "undefined behavior".
Of course Go has int64 and int32 types if you want to use them (and all conversions require explicit casting), but the default type for array lengths etc is the platform native type. But what is the better alternative?
Unfortunately I've read every comment in this HN thread (so far) and I haven't found any specific one mentioned, just people calling it "crazy" and "confusing" over and over.
Go is a language that has explicit pointers! And of course those can be 32 or 64 bits and change struct sizes, field offsets etc. There are certainly things about Go that are confusing but I've never thought this was one of them.
Just FYI Go has int8, int16, int32, int64, and int types. Only the 'int' type is the machine-native one. It's always been obvious to me that 'int' is the one without a fixed size, but I think this would be less obvious if the naming convention was different using short, int, long etc.
I don't know how many users are still on 32bit. Maybe low power network devices? Older mobile phones (Go isn't usually run on mobile, but it's possible...).
If at some point in the future 32 bit systems become totally irrelevant for Go users, maybe a release of Go could drop support for it altogether and make int an alias for int64, and save people some type casts. 32 bit systems probably aren't at that point yet though.
Well, 16 bit would also be possible if someone took the effort. Perhaps it just shouldn't be encouraged.
But I still think that native types have their place. It can often be quite reasonable to accept serious limitations on narrow machines where the performance gains won by the trade-off are desperately needed, lift those limitations on wider machines and grant an enormous safety margin on the widest architectures. When you dive deeper, the values where this makes sense still tend to be "index-like", as GP stated, but they don't have to strictly be indexes (e.g. IDs, a wider machine will be capable of working on bigger datasets and therefore be more likely to exhaust a given width of IDs, all indexes are identifiers but not all identifiers are indexes)
My C is a bit rusty but why should #defines be used in this scenario? A typedef set in a config.h.in is more than enough to meet the requirements of this usecase.