David Kröll

Posted on

IEnumerables transferred to Golang

There is no doubt that the `IEnumerable` type from C# is very powerful.
It's especially effective when the data we would like to iterate has yet to be aquired or calculated.

To state a matching example, the fibonacci sequence is one of them.
There we have a sequence with numbers depending on the numbers beforehand. So the next number is the sum of the two previous ones. I guess everyone is familiar with this sequence - here it is for numbers up to 100:

``````1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89
``````

When calculating this sequence and storing it using an `Array` datatype, we would have to generate these numbers and allocate memory.
Furthermore, we would have to figure out how many fibonacci numbers we would like to generate beforehand,
because the fibonacci sequence is basically infinite, but our `Array` is not.

Of course, one could do this without allocating a new `Array`, but then you have to implement the generation code in the same method.

``````static void Main(string[] args)
{
// a loop for printing the numbers
foreach (var x in GetNumbersArray(12))
{
if (x > 100)
break;
Console.WriteLine(x);
}
}

static int[] GetNumbersArray(int i)
{
int[] result = new int[i];

var (a, b) = (0, 1);

// and another loop for generating the numbers
for (var counter = 1; counter < i; counter++)
{
// logic without allocating a new array
// would have to be implemented here
(a, b) = (a + b, a);
result[counter] = a;
}

return result;
}
``````

Here the C# `IEnumerable` type helps out. It combines the best of the two worlds. No extra memory has to be allocated and the computation runs as we request the next item. In addition, no loops takes place more than once (as it is the case with the `Array`).

So the solution using the `IEnumerable` would look like this:

``````static void Main(string[] args)
{
// request the next number by iterating
foreach (var x in GetNumbers())
{
// break the loop when we reach more than 100
if (x > 100)
break;
Console.WriteLine(x);
}
}

static IEnumerable<int> GetNumbers()
{
var (a, b) = (0, 1);

while (true)
{
(a, b) = (a + b, a);
// return the next number as soon as requested
// using the yield statement
yield return a;
}
}
``````

No memory gets allocated to store the sequence, and the caller of the `GetNumbers()` could decide when to break the loop.

Translating to go

In Go we do not have something like the `IEnumerable` in C#, in fact we have no chance to create a custom type that allows us to be iterated by implementing an interface. In C# we could achieve this by using the `IEnumerator` interface. Of course we do not have to reinvent the wheel, everything we have to do is rebuild the interface from scratch using custom types and access methods.

``````type Fibonacci struct {
a int
b int
}

// Get return the current value to print
func (f *Fibonacci) Get() int {
return f.a
}

// Next calculates the next value
// and returns false if there are no more values
func (f *Fibonacci) Next() bool {
f.a, f.b = f.a+f.b, f.a
return true
}

// NewFibonacci initializes a new Fibonacci type
func NewFibonacci() Fibonacci {
return Fibonacci{
a: 0,
b: 1,
}
}

func main() {
sequence := NewFibonacci()

for sequence.Next() {
if x := sequence.Get(); x < 100 {
fmt.Println(x)
} else {
break
}
}

}
``````

This is now the same way as C# `IEnumerator` is implemented under the hood. We are able to iterate using the `Next()` method and access the current value using the `Get()`method. It perfectly fits our needs and satisfies our requirements. No additional memory has to be allocated and no loop is executed twice. Using this blueprint, every usecase for `IEnumerable` can be solved in Go. In fact, it is already in use in Go's standard library.

One example implementation would be the `bufio.Scanner` type - https://pkg.go.dev/bufio#Scanner

Top comments (2)

Venkatesh KL

A nice example. I'm not familiar with C# but that looks relatable. Would you mind if i reference this article & create another article with same context but in reference to JavaScript??

David Kröll

No, I am, absolute looking forward to this! Thanks for your kudos.