Custom index arithmetic

This is a clarifying question about custom indexing.

If x::AbstractVector has a conforming interface, can I assume that that the valid indexes are firstindex(x):lastindex(x), ie they are

  1. integers (so nothing wacky like Float64s or Symbol),
  2. forming a contiguous range, so I can do arithmetic on them, at least index += some_step.

This is for an algorithm that I find difficult to express in an iterator form using eachindex.

Sorry if this is obvious and already clearly discussed in the manual.

We currently assume that axes(A::AbstractArray, i) <: AbstractUnitRange in lots of code (so even tighter than your range assumption), and we also expect axes to have integer elements. I think you’re safe to assume the same. A custom “array” that breaks this will already be in lots of deep water and I’d say they’re fine swimming out there on their own.

Thanks. Do you think it might be worth documenting this explicitly? I would then open an issue.

I also asked myself from time to time what should be assumed about AbstractArray indices. I guess its a common question, when writing generic code. So I think it would be good to document this.

After a close reading of the Interfaces and Arrays with custom indices documentation, I saw that both are pretty specific about axes returning (tuples of) AbstractUnitRange.

I am under the impression that this allows non-integer types though: anything goes that has a oneunit, eg I could define

struct RealUnitRange{T <: Real} <: AbstractUnitRange{T}
    start::T
    stop::T
    len::Int
end

RealUnitRange(start, length::Integer) = RealUnitRange(start, start + length, length)

function Base.getindex(rur::RealUnitRange, i::Int)
    @boundscheck @assert 1 ≤ i ≤ rur.len
    rur.start + i
end

# ...

and it would be valid for axis to return this. Or the same exercise with, for example, Dates.Date.

This means that I cannot rely on arithmetic with integers; I should at least increment with multiples of oneunit. Also, in theory, I could be worried about floating point error and similar, since the eltype of the range could be anything.

Is this a correct interpretation?

Interesting — you might be right. It’s not something that I’ve personally seen or tried, so I wouldn’t be surprised if such an array would fail in some situations.

One thing we do assume is that A[axes(A)…] == A. This means that it also needs to be a valid index type. We also sometimes grab axes from one array and use them to index into another — so it needs to be a valid index type for all array types. It may be possible to extend to_index to make that work for a non-integer, but I’d still be surprised if everything just worked.

Perhaps restricting to AbstractUnitRange{<:Integer} for the time being could be a reasonable limitation for the time being.

I think this is worth a clarification, so I will open an issue.

https://github.com/JuliaLang/julia/issues/29062