Range over int

26,493 views
Skip to first unread message

Cyril Oblikov

unread,
Jan 19, 2012, 5:49:37 AM1/19/12
to golan...@googlegroups.com
Hi, guys. Sorry if it was already discussed.

Why isn't this code correct?

var N int = ...
for i := range N {
    doSmth(i)
}

In my opinion it looks much simpler than:

var N int = ...
for i := 0; i < N; i++ {
    doSmth(i)
}

Volker Dobler

unread,
Jan 19, 2012, 11:14:46 AM1/19/12
to golang-nuts
The "why not correct" can be answered very simple:
The spec doesn't allow it.
See http://golang.org/doc/go_spec.html#RangeClause

The question why the spec doesn't allow it is more
complex. It could be allowed I think, but the argument
"looks much simpler" might not convince e.g. a
programmer with a strong C background who thinks
that for i:=0;i<N;i++ looks way more natural and clear.
I think there is just no real need.

Volker

Ronnie Howell

unread,
Jan 19, 2012, 11:29:33 AM1/19/12
to golan...@googlegroups.com
Go does allow the range operator in this fashion, it's a matter of syntax in this case.  To get this to work you would need to use the following:
N := []int{...}
for index,element := range N {
  doSomethingWithIndex(index)
 doSomethingWithElement(element)

Cyril Oblikov

unread,
Jan 19, 2012, 12:05:50 PM1/19/12
to golan...@googlegroups.com
четверг, 19 января 2012 г. 19:14:46 UTC+3 пользователь Volker Dobler написал:
The question why the spec doesn't allow it is more
complex.  It could be allowed I think, but the argument
"looks much simpler" might not convince e.g. a
programmer with a strong C background who thinks
that for i:=0;i<N;i++ looks way more natural and clear.  

Maybe, but a programmer with strong Python background probably would prefer range :)
As far as I can see this feature is obvious enough and will not mislead anyone. 

Paul Borman

unread,
Jan 19, 2012, 12:32:35 PM1/19/12
to golan...@googlegroups.com
The two forms are not interchangeable.

for i := 0; i < 5; i++ {
   println(i)
   i = 5
}

(prints one line)

and

for i := range 5 {
    println(i)
    i = 5
}

would likely do different things.  I would assume the latter would work like:

for i := range make([]int, 5) {
    println(i)
    i = 5
}

(prints 5 lines)

I do admit this would be nice, but what about ranging from 1 to N instead of 0 to N, or ranging from -5 to 5?  You would end up with something like

for i := range [ start, stop] {
}

where range N is a shorthand for range [0, N-1] unless you assume range [1,5] gives you 1,2,3,4.  You might as well go full on sets so you can say

range [-2...2] ->  -2, -1, 0, 1, 2
range [0...3) -> 0, 1, 2
range [1, 3, 5...7] -> 1, 3, 5, 6, 7
range (0...3) -> 1, 2
range (0, 3) -> nothing

I think the argument would be made that you can already do all of these things with the existing specification.  So while I agree range N would simplify some code (and I have even typed it a few times), I think it would open up more questions.  Currently you can only range on things that can produce a set of values.  From that viewpoint range N would simply degenerate down to N.

    -Paul

Steven Blenkinsop

unread,
Jan 19, 2012, 12:37:14 PM1/19/12
to golan...@googlegroups.com, golan...@googlegroups.com
The traditional for clause is more flexible as it can define upper and lower bounds, a step, or even more complex   iteration behaviours. Ranging over an int provides only 0 →n - 1, which can easily be expressed using the traditional for clause, and very often can be replaced with a range over a slice. So, in terms of gain in expressive power, this feature doesn't seem like it would pull its weight. It's not about whether it's obvious, you still have to specify it.

Dmitry Vyukov

unread,
Jan 19, 2012, 12:38:36 PM1/19/12
to Paul Borman, golan...@googlegroups.com
2012/1/19 Paul Borman <bor...@google.com>

Dmitry Vyukov

unread,
Jan 19, 2012, 12:39:43 PM1/19/12
to Paul Borman, golan...@googlegroups.com
2012/1/19 Paul Borman <bor...@google.com>

The two forms are not interchangeable.

for i := 0; i < 5; i++ {
   println(i)
   i = 5
}

(prints one line)

and

for i := range 5 {
    println(i)
    i = 5
}

would likely do different things.  I would assume the latter would work like:

for i := range make([]int, 5) {
    println(i)
    i = 5
}


for i := range [10] struct{}{} {
    ...
}

:)

Brad Fitzpatrick

unread,
Jan 19, 2012, 12:43:28 PM1/19/12
to Dmitry Vyukov, Paul Borman, golan...@googlegroups.com
On Thu, Jan 19, 2012 at 9:39 AM, Dmitry Vyukov <dvy...@google.com> wrote:
2012/1/19 Paul Borman <bor...@google.com>
The two forms are not interchangeable.

for i := 0; i < 5; i++ {
   println(i)
   i = 5
}

(prints one line)

and

for i := range 5 {
    println(i)
    i = 5
}

would likely do different things.  I would assume the latter would work like:

for i := range make([]int, 5) {
    println(i)
    i = 5
}


for i := range [10] struct{}{} {
    ...
}

:)

You're a sick man, Dmitry.

Sebastien Binet

unread,
Jan 19, 2012, 12:45:58 PM1/19/12
to Cyril Oblikov, golan...@googlegroups.com

well, it is also rather easy to create an 'itertools' package with a Range
function returning a slice with the data, and an IRange function
returning a channel to the data (when the slice begins to be too big)

package itertools

func Range(args ...int) []int {
nargs := len(args)
start, stop, stride := 0, 0, 0
switch nargs {
case 1:
start = 0
stop = args[0]
stride= 1
case 2:
start = args[0]
stop = args[1]
stride= 1
case 3:
start = args[0]
stop = args[1]
stride= args[2]
default:
panic("boo")
}
out := []int{}
for i := start; i < stop; i += stride {
out = append(out, i)
}
return out
}
// EOF //


then:
for i := range itertools.Range(10) {
fmt.Println(i)
}

-s

Dmitry Vyukov

unread,
Jan 19, 2012, 12:46:57 PM1/19/12
to Brad Fitzpatrick, Paul Borman, golan...@googlegroups.com
Is it sickness? Go does not allow me to show the real sickness. It was a way more funny with C++ templates.

minux

unread,
Jan 19, 2012, 12:47:36 PM1/19/12
to Dmitry Vyukov, golan...@googlegroups.com

2012/1/20 Dmitry Vyukov <dvy...@google.com>

for i := range [10] struct{}{} {
    ...
}

:)
The magical 'struct {}'! This kind of trick might be worth mention in effective_go
or go-wiki? Or this is considered not that good coding practice?

Dmitry Vyukov

unread,
Jan 19, 2012, 12:49:22 PM1/19/12
to Sebastien Binet, Cyril Oblikov, golan...@googlegroups.com
On Thu, Jan 19, 2012 at 9:45 PM, Sebastien Binet <seb....@gmail.com> wrote:
On Thu, 19 Jan 2012 09:05:50 -0800 (PST), Cyril Oblikov <mun...@gmail.com> wrote:
> четверг, 19 января 2012 г. 19:14:46 UTC+3 пользователь Volker Dobler
> написал:
> >
> > The question why the spec doesn't allow it is more
> > complex.  It could be allowed I think, but the argument
> > "looks much simpler" might not convince e.g. a
> > programmer with a strong C background who thinks
> > that for i:=0;i<N;i++ looks way more natural and clear.
> >
>
> Maybe, but a programmer with strong Python background probably would prefer
> range :)
> As far as I can see this feature is obvious enough and will not mislead
> anyone.

well, it is also rather easy to create an 'itertools' package with a Range
function returning a slice with the data, and an IRange function
returning a channel to the data (when the slice begins to be too big)

If you use that funny []struct{} then it is never too big :)

Brad Fitzpatrick

unread,
Jan 19, 2012, 12:49:53 PM1/19/12
to minux, Dmitry Vyukov, golan...@googlegroups.com
This is perfectly efficient, but not idiomatic.  It probably doesn't deserve to get promoted.

 

roger peppe

unread,
Jan 19, 2012, 1:07:20 PM1/19/12
to Dmitry Vyukov, Paul Borman, golan...@googlegroups.com
2012/1/19 Dmitry Vyukov <dvy...@google.com>:

> 2012/1/19 Paul Borman <bor...@google.com>
>>
>> The two forms are not interchangeable.
>>
>> for i := 0; i < 5; i++ {
>>    println(i)
>>    i = 5
>> }
>>
>> (prints one line)
>>
>> and
>>
>> for i := range 5 {
>>     println(i)
>>     i = 5
>> }
>>
>> would likely do different things.  I would assume the latter would work
>> like:
>>
>> for i := range make([]int, 5) {
>>     println(i)
>>     i = 5
>> }
>
>
>
> for i := range [10] struct{}{} {

dammit, you got there first!

Gustavo Niemeyer

unread,
Jan 19, 2012, 1:21:36 PM1/19/12
to minux, Dmitry Vyukov, golan...@googlegroups.com
> The magical 'struct {}'! This kind of trick might be worth mention in
> effective_go or go-wiki? Or this is considered not that good
> coding practice?

It's fun, but it's *horrible* coding practice. Kittens will die if you use it.

for i := range [10] struct{}{} {

for i := 0; i < 10; i++ {

Shorter, more flexible, and recognizable since decades ago.

--
Gustavo Niemeyer
http://niemeyer.net
http://niemeyer.net/plus
http://niemeyer.net/twitter
http://niemeyer.net/blog

-- I'm not absolutely sure of anything.

Sebastien Binet

unread,
Jan 19, 2012, 1:22:23 PM1/19/12
to Dmitry Vyukov, Cyril Oblikov, golan...@googlegroups.com
On Thu, 19 Jan 2012 21:49:22 +0400, Dmitry Vyukov <dvy...@google.com> wrote:
> On Thu, Jan 19, 2012 at 9:45 PM, Sebastien Binet <seb....@gmail.com>wrote:
>
> > On Thu, 19 Jan 2012 09:05:50 -0800 (PST), Cyril Oblikov <mun...@gmail.com>
> > wrote:
> > > четверг, 19 января 2012 г. 19:14:46 UTC+3 пользователь Volker Dobler
> > > написал:
> > > >
> > > > The question why the spec doesn't allow it is more
> > > > complex. It could be allowed I think, but the argument
> > > > "looks much simpler" might not convince e.g. a
> > > > programmer with a strong C background who thinks
> > > > that for i:=0;i<N;i++ looks way more natural and clear.
> > > >
> > >
> > > Maybe, but a programmer with strong Python background probably would
> > prefer
> > > range :)
> > > As far as I can see this feature is obvious enough and will not mislead
> > > anyone.
> >
> > well, it is also rather easy to create an 'itertools' package with a Range
> > function returning a slice with the data, and an IRange function
> > returning a channel to the data (when the slice begins to be too big)
> >
>
> If you use that funny []struct{} then it is never too big :)

right, but then the start/stop/stride aren't relevant anymore, are they ?

ie:

for _,v := range itertools.Range(2,10,3) {
fmt.Print("v=",v,"\n")
}


-s

--
#########################################
# Dr. Sebastien Binet
# Laboratoire de l'Accelerateur Lineaire
# Universite Paris-Sud XI
# Batiment 200
# 91898 Orsay
#########################################

Dmitry Vyukov

unread,
Jan 19, 2012, 2:24:22 PM1/19/12
to Gustavo Niemeyer, minux, golan...@googlegroups.com
On Thu, Jan 19, 2012 at 9:21 PM, Gustavo Niemeyer <gus...@niemeyer.net> wrote:
> The magical 'struct {}'! This kind of trick might be worth mention in
> effective_go or go-wiki? Or this is considered not that good
> coding practice?

It's fun, but it's *horrible* coding practice. Kittens will die if you use it.

 for i := range [10] struct{}{} {
 for i := 0; i < 10; i++ {
 
Shorter, more flexible, and recognizable since decades ago.

If we apply roger peppe's proposal that struct{}{} is nil, then it becomes:

 for i := range[10]nil {
 for i := 0; i < 10; i++ {

2 symbol win :)

Gustavo Niemeyer

unread,
Jan 19, 2012, 2:27:56 PM1/19/12
to Dmitry Vyukov, minux, golan...@googlegroups.com
On Thu, Jan 19, 2012 at 17:24, Dmitry Vyukov <dvy...@google.com> wrote:
> If we apply roger peppe's proposal that struct{}{} is nil, then it becomes:
>
>  for i := range[10]nil {
>  for i := 0; i < 10; i++ {
>
> 2 symbol win :)

It's getting better and better.. :)

roger peppe

unread,
Jan 19, 2012, 2:28:50 PM1/19/12
to Dmitry Vyukov, Gustavo Niemeyer, minux, golan...@googlegroups.com

for the record, i wasn't proposing that struct{}{} to *be* nil, just
that for it to be compatible with nil (the same as it's compatible
with *Foo interface{} and chan bool, without "being" any of those types).

Michael Jones

unread,
Jan 19, 2012, 10:57:54 AM1/19/12
to golan...@googlegroups.com
Looks nice visually. Since 'range' is used to mean "each element within" then you would need to believe that people will understand that [0..N-1] means "within" N -- that starting at 1 or ending at N are not to be expected. 

Would this be equally clear with more complicated expressions?
    for i := range IndexOfCharacter(s, '[') { // process characters up to '['
--
Michael T. Jones | Chief Technology Advocate  | m...@google.com |  +1 650-335-5765

Miguel Pignatelli

unread,
Jan 24, 2012, 7:40:13 AM1/24/12
to golan...@googlegroups.com
On 19/01/12 17:47, minux wrote:
>
> 2012/1/20 Dmitry Vyukov <dvy...@google.com <mailto:dvy...@google.com>>

Hmmm... I am not planning to use this construct, but I am puzzled about
this behaviour of struct{}.
What is the "magical 'struct {}'".

M;

chris dollin

unread,
Jan 24, 2012, 7:41:24 AM1/24/12
to Miguel Pignatelli, golan...@googlegroups.com
On 24 January 2012 12:40, Miguel Pignatelli <eme...@gmail.com> wrote:

>
> Hmmm... I am not planning to use this construct, but I am puzzled about this
> behaviour of struct{}.
> What is the "magical 'struct {}'".

A struct{} takes up no room.

Chris

--
Chris "allusive" Dollin

Message has been deleted

Andrew Walker

unread,
Jan 23, 2014, 3:44:10 AM1/23/14
to golan...@googlegroups.com
I know this is a super old thread, but it's still the first thing that comes up if someone googles "golang range int" and I imagine a lot of people might be coming to the language from perl, python or ruby where you can do 

"for (1..5) { #stuff }"
"for _ in range(5)"
and
"5.times {}"

respectively, and are looking for an easy and clean-looking way to do it in Go.

I saw someone created an "itertools", which returns a slice, but I'm surprised no one mentioned channels.  It seems to me it would be much more efficient and idiomatic to use a closure/goroutine/channel.  That way you're not creating an array of any sort or (eesh!) creating x new slices as we see in that example.

Something like this perhaps?  http://play.golang.org/p/VcW3EMnj9t

func numRange(first int, rest ...int) chan int {
out := make(chan int)
var start, end int
if len(rest) == 0 {
start = 1
end = first
} else {
start = first
end = rest[0]
}
go func() {
for i := start; i <= end; i++ {
out <- i
}
close(out)
}()
return out
}

And then you can just:

for s := range numRange(5) {
fmt.Println(s)
}
for s := range numRange(5, 10) {
fmt.Println(s)
}
for _ = range numRange(1, 3) {
fmt.Println("bar")
}

Unless there's something I'm missing and there's already a standard library way to do this (which is very possible!)

chris dollin

unread,
Jan 23, 2014, 3:47:59 AM1/23/14
to Andrew Walker, golang-nuts
On 23 January 2014 08:41, Andrew Walker <walk...@gmail.com> wrote:

I know this is a super old thread, but it's still the first thing that comes up if someone googles "golang range int" and I imagine a lot of people might be coming to the language from perl, python or ruby where you can do 

"for (1..5) { #stuff }"
"for _ in range(5)"
and
"5.times {}"

respectively, and are looking for an easy and clean-looking way to do it in Go.


I don't see what's non-easy/unclean about

    for i := 1; i <= 5; i ++ { whatever() }

Dmitry Vyukov

unread,
Jan 23, 2014, 3:48:25 AM1/23/14
to Andrew Walker, golang-nuts
what's wrong with for i := 0; i < 10; i++ ?

On Thu, Jan 23, 2014 at 12:41 PM, Andrew Walker <walk...@gmail.com> wrote:
> I know this is a super old thread, but it's still the first thing that comes
> up if someone googles "golang range int" and I imagine a lot of people might
> be coming to the language from perl, python or ruby where you can do
>
> "for (1..5) { #stuff }"
> "for _ in range(5)"
> and
> "5.times {}"
>
> respectively, and are looking for an easy and clean-looking way to do it in
> Go.
>
> I saw someone created an "itertools", which returns a slice, but I'm
> surprised no one mentioned channels. It seems to me it would be much more
> efficient and idiomatic to use a closure/goroutine/channel. That way you're
> not creating an array of any sort or (eesh!) creating x new slices as we see
> below.
> --
> 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...@googlegroups.com.
> For more options, visit https://groups.google.com/groups/opt_out.

Jan Mercl

unread,
Jan 23, 2014, 3:48:27 AM1/23/14
to Andrew Walker, golang-nuts
On Thu, Jan 23, 2014 at 9:41 AM, Andrew Walker <walk...@gmail.com> wrote:
> "for (1..5) { #stuff }"
> "for _ in range(5)"
> and
> "5.times {}"

package main // http://play.golang.org/p/-5FIKdPVgg

import (
"fmt"
)

var n [int(^uint(0) >> 1)]struct{}

func main() {
for i := range n[:5] {
fmt.Println(i, "Hello")
}
}

-j

Michael Jones

unread,
Jan 23, 2014, 10:36:22 AM1/23/14
to Andrew Walker, golang-nuts
On Thu, Jan 23, 2014 at 12:41 AM, Andrew Walker <walk...@gmail.com> wrote:
"for (1..5) { #stuff }"
"for _ in range(5)"
and
"5.times {}"

respectively, and are looking for an easy and clean-looking way to do it in Go.

for i := 0; i <5; i++ {
    stuff
}

is the easy and clean-looking way to do it in Go.

RickyS

unread,
Jan 23, 2014, 11:37:10 AM1/23/14
to golan...@googlegroups.com, Paul Borman
A more stylish version at http://play.golang.org/p/HR4tFDGAFl


type T [10]struct{}
var l10 T

func main() {
    fmt.Printf("type %T, value is %v\n----------\n", l10, l10)
    for j := range l10 {
        fmt.Printf("  %d.   ", j)
    }
}

John Souvestre

unread,
Jan 23, 2014, 10:55:40 PM1/23/14
to Jan Mercl, golang-nuts
Hello Jan.

> for i := range n[:5] { ... }

Nice! Clearer and easier.

John

John Souvestre - New Orleans LA - (504) 454-0899




Brad Fitzpatrick

unread,
Jan 23, 2014, 11:23:41 PM1/23/14
to RickyS, golang-nuts, Paul Borman
I like:


    func main() {
         for i := range iter.N(10) {
             println(i)
         }
    }

No allocations. No risk of blocked sending goroutines.



Rob Pike

unread,
Jan 23, 2014, 11:50:27 PM1/23/14
to Brad Fitzpatrick, RickyS, golang-nuts, Paul Borman
I don't like.

Rob Pike

unread,
Jan 23, 2014, 11:58:24 PM1/23/14
to Brad Fitzpatrick, RickyS, golang-nuts, Paul Borman
I should explain. A for loop is called a for loop because looping is
what it is for. So use a for for what it's intended for: a loop.

It seems that almost every time someone comes up with a way to avoid
doing something like a for loop the idiomatic way, because it feels
too long or cumbersome, the result is almost always more keystrokes
than the thing that is supposedly shorter. In this case (and there are
others)

for i := range iter.N(5)

is one keystroke longer than

for i := 0; i < 5; i++

_before_ you count the cost of typing the import.

That's leaving aside all the crazy overhead these "improvements" bring.

Sorry, I know this is a joke thread but I have seen real examples of
this general style of making things "nicer", by making them much
worse. And they take time to stamp out, as if one can ever succeed.

But sure, have fun.

-rob

Brad Fitzpatrick

unread,
Jan 24, 2014, 12:00:22 AM1/24/14
to Rob Pike, RickyS, golang-nuts, Paul Borman
You didn't count the shift keys.

(Sorry... :-))

Brad Fitzpatrick

unread,
Jan 24, 2014, 12:04:58 AM1/24/14
to Rob Pike, RickyS, golang-nuts, Paul Borman
Actually, on a somewhat serious point... you wrote:

> _before_ you count the cost of typing the import.

But I haven't written import lines for months now.  And interestingly, having goimports has actually changed my aversion towards creating new small packages.  Now I have no fear of making a new package because it won't be annoying to use.  I had a tendency to dump too much into util-style packages before, rather than placing it where it belongs.

If I actually wanted to seriously use this "iter" joke package, I'd just type "range iter.N(5)" and it would work on save.  And I saved two shift keys.  Or four keys overall if I rename the package "to".

Rob Pike

unread,
Jan 24, 2014, 12:17:17 AM1/24/14
to Brad Fitzpatrick, RickyS, golang-nuts, Paul Borman
Sure, but that says nothing about the run-time overhead and the
compiler-opitimization-defeating obfuscation.

I'll stop. I realized earlier this week that the metric for software
quality today has almost nothing to do with the issues I believe to
matter. For instance, a newline is worth at least 100 other
keystrokes, for some unknown reason.

Seriously, I'll stop.

-rob

John

unread,
Jan 24, 2014, 12:38:09 AM1/24/14
to Rob Pike, Brad Fitzpatrick, RickyS, golang-nuts, Paul Borman
Perhaps the appeal of Brad’s approach is less repetition at the cost of length, and appearing less selfish.

:)

Henrik Johansson

unread,
Jan 24, 2014, 1:57:12 AM1/24/14
to John, golang-nuts, Brad Fitzpatrick, RickyS, Rob Pike, Paul Borman

Sublime auto generates a standard for loop so there is only the upper bound to fill in. :-)

Kevin Gillette

unread,
Jan 24, 2014, 2:26:30 AM1/24/14