Range Over Iterators in Julia

Our example demonstrates how to iterate over structures with support for iterators, including defining custom iterators. Below is the full source code in Julia.

struct Element{T}
    val::T
    next::Union{Nothing, Element{T}}
end

struct List{T}
    head::Union{Nothing, Element{T}}
    tail::Union{Nothing, Element{T}}
end

function List{T}() where T
    List{T}(nothing, nothing)
end

function push!(lst::List{T}, v::T) where T
    if lst.tail === nothing
        lst.head = Element{T}(v, nothing)
        lst.tail = lst.head
    else
        lst.tail.next = Element{T}(v, nothing)
        lst.tail = lst.tail.next
    end
end

function iterate_element(e::Union{Nothing, Element{T}}, state=nothing) where T
    return e === nothing ? nothing : (e.val, e.next)
end

function Base.iterate(lst::List{T}, state=nothing) where T
    iterate_element(lst.head, state)
end

function gen_fib()
    a, b = 1, 1
    return () -> begin
        val = a
        a, b = b, a + b
        return val
    end
end

In Julia, iterators work with the Base.iterate function. The example includes a custom linked list and an infinite Fibonacci series generator.

Let’s create a list and push some values into it:

lst = List{Int}()
push!(lst, 10)
push!(lst, 13)
push!(lst, 23)

We can then iterate over the list using a for loop:

for e in lst
    println(e)
end

Julia provides useful packages and functions to work with iterators. Additionally, we demonstrate an infinite Fibonacci sequence generator:

fib = gen_fib()
for n in Iterators.take(fib, 10)
    println(n)
end

Running the code will produce the following output:

10
13
23
1
1
2
3
5
8
13
21

This example shows how Julia’s iterator protocol can handle both finite and infinite sequences.

Next example: Errors