tl;dr: Go's "range" operator has eliminated the most common trap where
I make off-by-one errors.  The next largest category of off-by-one
errors would be eliminated if there was a way to specify the last item
in an array.  It would also improve a developer's ability to convey
intent.

...

I've been coding for a few years (first line of BASIC was in September
1979) and I have to admit that off-by-one errors have always been a
problem for me.  I guess I'm just that kind of sloppy.

Speaking for myself, the biggest category of potential off-by-one
errors is when constructing "for" loops.  Go's "range" operator
eliminates that entire category of potential mistakes.  For that I'm
thankful.

So what is my next largest category?

Again, speaking only for myself, it is the fact that the last item in
an array is len(a)-1. It's easy to remember to subtract 1, but when
it's buried in the middle of a formula, it's non-obvious and obscured.
More importantly, it hides intent.

For example often the -1 is cancelled out by a +1 elsewhere in the
formula.  Now that was originally len(a)-1-+1-y becomes len(a)-y.
Original intent obscured.

Or worse, a formula like len(a)-1-y becomes len(a)-z because someone
noticed that z==y-1 and made the substitution. Yes, it is a premature
optimization but I bet you’ve done a similar substitution many times.

Oh wait. Did you notice the typo in the previous paragraph? The
substitution would be valid if z==y+1, not z==y-1.

The fact that you didn’t notice proves my point. Now do you understand my pain?

Luckily I have a simple proposal that would fix this.

What if there was a built in function highest() that was like len(),
but returns the highest valid index. Sure, it is nothing more than a
substitute for len(a)-1. Not a super huge difference, but I think it
would eliminate this class of off-by-one errors.

My example from before becomes:

OLD: len(a)-1-+1-y
NEW: highest(a)+1-y`

The second example loses the temptation for premature optimization:

OLD: len(a)-1-y
NEW: highest(a)-y

Example: The classic Go “Pop from a stack” idiom is improved:

OLD: x, a = a[len(a)-1], a[:len(a)-1]
NEW: x, a = a[highest(a)], a[:len(a)-1]
Note: Intention is clearer: x is the last element, a has a reduction
in the length by 1.
NEW2: x, a = a[highest(a)], a[:highest(a)]
In this case the intention is even more clear: “x is the last element,
a is all but the last element.”

Loop through elements in reverse order:

OLD: for i := len(s)-1; i >= 0; i-- {
NEW: for i := highest(s); i >= 0; i-- {
Note: The intention is much clearer.


I realize that the go project is very reluctant to add new features
and rightfully so.  That said, I think highest() (or maybe end(),
ultimate()?  final(), hind()?) would be a great human-centric feature
to add.

Tom
WIth better formatting... https://www.yesthatblog.com/post/0068-go-off-by-one

-- 
Email: t...@whatexit.org    Work: tlimonce...@stackoverflow.com
Twitter: YesThatTom
Blog: https://www.yesthatblog.com

-- 
You received this message because you are subscribed to the Google Groups 
"golang-nuts" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to golang-nuts+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/golang-nuts/CAHVFxgmi3WLfiEsTyiXPtXHs57_o_n86C-e-nHpgXgG8y%2BKYDA%40mail.gmail.com.

Reply via email to