Bring back foreach int indexes

Jonathan M Davis newsgroup.d at jmdavisprog.com
Thu Nov 30 15:25:52 UTC 2023


On Thursday, November 30, 2023 7:31:42 AM MST bachmeier via Digitalmars-d 
wrote:
> On Thursday, 30 November 2023 at 11:54:01 UTC, IGotD- wrote:
> > On Thursday, 30 November 2023 at 11:01:15 UTC, Hipreme wrote:
> >> That is because there is no reason to have explicit
> >> conversions everywhere, they are overly verbose and ugly. And
> >> this is one of the reasons because I don't use `foreach` in my
> >> code. Good old `for` loop can let you decide the type. Modern
> >> programming languages should reduce friction, and not increase
> >> it.
> >
> > One of the corner stones in D is that there are hardly any
> > implicit conversions. I not saying personally against it but it
> > is one of the main D "features".
>
> Do you have an example where it can cause problems in this case?

The main place that using int with the index for foreach would cause
problems is if you're programming on a 32-bit system and then later start
compiling that code on a 64-bit system.

Because size_t is uint on 32-bit systems, using int with foreach works just
fine aside from the issue of signed vs unsigned (which D doesn't consider to
be a narrowing conversion, for better or worse). So, someone could use int
with foreach on a 32-bit system and have no problems, but when they move to
a 64-bit system, it could become a big problem, because there, size_t is
ulong. So, code that worked fine on a 32-bit system could then break on a
64-bit system (assuming that it then starts operating on arrays that are
larger than a 32-bit system could handle).

If the implicit narrowing conversion is forbidden with foreach (like it is
pretty much everywhere else in the language), and you used int or uint for
the index, then when you go to compile that code on a 64-bit system, then
you'll get a compiler error, and you can then fix the code as appropriate,
whereas if we allow it to be int or uint and treat it like an explicit cast,
then you have a silent bug.

And because there wasn't actually an explicit cast involved, you can't even
grep for the cast keyword. There's also no way to tell at a glance whether
the programmer purposefully used int knowing that the conversion would take
place or whether they just used int because that worked for what they were
doing on a 32-bit system but then didn't work later on 64-bit systems (and
of course, if the code worked with int on a 64-bit system originally doesn't
mean that it will later, since the code that generates the array could later
be changed and allow much larger arrays than was originally the case).

Of course, if you're using int instead of uint, then you actually still risk
bugs on 32-bit systems if the array is large enough (depending on what
exactly is done with the index), but the core idea of disallowing the
implicit conversion with foreach is to force the programmer to deal with the
issue rather than silently having code that could have an index that's
larger than the type being used can actually hold.

Now, the question then is how likely that particular bug is vs someone
purposefully using int on a 64-bit system, because they wanted int, and they
didn't think that there was any way that they would be operating on an array
that's larger than int.max or uint.max in length. Outside of operating on
large files and "big data," there probably aren't a lot of programs that are
going to have arrays large enough that int won't be enough to index them, so
there will be a lot of code which could use int with foreach and have no
problems whatsoever. And clearly, there are folks who have used int with
foreach in the past and had no problems with it, so they're annoyed at being
forced to use size_t instead.

Personally, I tend to lean towards the pendatic approach here and think that
anything and everything involving indexing should be using size_t so that it
will work across architectures and not have bugs due to casting to smaller
types, but obviously, smart people can disagree on the issue. It's not like
allowing int in foreach is wrong across the board. It's just that it can
cause a particular class of bugs, and arguably, it's an implicit conversion
rather than an explicit conversion, which goes against how D's type
conversion rules normally work. But it's also the case that using int in
foreach was allowed and treated as an explicit cast for years.

So, I think that the change to requiring size_t was a good one, but I can
also see why some folks would get annoyed by it, and it's a much bigger deal
than it would have been otherwise, because the behavior was changed rather
than it always having required a type that size_t can implicitly convert to.

- Jonathan M Davis





More information about the Digitalmars-d mailing list