# What is the most idiomatic way to implement bounds checking in `getindex` for arbitrary types?

Specifically, I have two main requirements in mind.

1. It should throw `BoundsError` with complete information.
2. The `@inbounds` macro should work as intended for this type, i.e. elide the bounds checking entirely

Item 1 seems straightforward, just conditionally throw a `BoundsError` with the right arguments. But, it seems that I am not sure about item 2; how does one check if `@inbounds` is bypassing bounds checking in the first place? I tried using `@code_warntype` and friends on `@inbounds foo[i]` where `foo isa Foo`, but it seems to give info about the operations of the inbounds macro itself.

A toy example.

``````struct Foo{T}
x::Vector{T}
y::Vector{Int}
end

import Base.getindex

function Base.getindex(f::Foo{T}, i::Int) where {T}
idx = f.y[i]
if idx  > length(f.x)
throw(BoundsError(x, idx))
end
f.x[idx]
end
``````

How can one transform this code to make `@inbounds` work as intended, and how does one check if it is working as intended?

``````Base.@propagate_inbounds function Base.getindex(f::Foo{T}, i::Int) where {T}
idx = f.y[i]
Base.@boundscheck idx in eachindex(f.x) || throw(BoundsError(x, idx))
f.x[idx]
end

julia> function force_getindex(x, i)
@inbounds x[i]
end;

julia> x, y = [1, 2, 3], [1, 2, 3];

julia> resize!(x, 2);

julia> foo = Foo(x, y);

julia> foo[1]
1

julia> foo[2]
2

julia> foo[3]
ERROR: BoundsError: attempt to access 2-element Vector{Int64} at index [3]
Stacktrace:
[1] getindex(f::Foo{Int64}, i::Int64)
@ Main ./REPL[2]:3
[2] top-level scope
@ REPL[12]:1

julia> force_getindex(foo, 3)
3
``````

Better to do

``````@boundscheck checkbounds(f.x, idx)
``````

(Although if you do `f.x[idx]` it does the bounds check anyway, so Iām not sure why the test is needed at all.)

1 Like

I guess particular example is too simple. The explicit bounds check logic that I am actually working on is bit more involved; even if the explicit check fails, the index would still point to a valid memory location i.e. would pass the bounds checking of the default types.

Should I update the question to include a more representative example?