Like many modern programming languages, Julia uses bounds checking to ensure program safety when accessing arrays. In tight inner loops or other performance critical situations, you may wish to skip these bounds checks to improve runtime performance. For instance, in order to emit vectorized (SIMD) instructions, your loop body cannot contain branches, and thus cannot contain bounds checks. Consequently, Julia includes an@inbounds(...)
macro to tell the compiler to skip such bounds checks within the given block. User-defined array types can use the@boundscheck(...)
macro to achieve context-sensitive code selection.
The@boundscheck(...)
macro marks blocks of code that perform bounds checking. When such blocks are inlined into an@inbounds(...)
block, the compiler may remove these blocks. The compiler removes the@boundscheck
blockonly if it is inlined into the calling function. For example, you might write the methodsum
as:
function sum(A::AbstractArray) r = zero(eltype(A)) for i in eachindex(A) @inbounds r += A[i] end return rend
With a custom array-like typeMyArray
having:
@inline getindex(A::MyArray, i::Real) = (@boundscheck checkbounds(A, i); A.data[to_index(i)])
Then whengetindex
is inlined intosum
, the call tocheckbounds(A, i)
will be elided. If your function contains multiple layers of inlining, only@boundscheck
blocks at most one level of inlining deeper are eliminated. The rule prevents unintended changes in program behavior from code further up the stack.
It is easy to accidentally expose unsafe operations with@inbounds
. You might be tempted to write the above example as
function sum(A::AbstractArray) r = zero(eltype(A)) for i in 1:length(A) @inbounds r += A[i] end return rend
Which quietly assumes 1-based indexing and therefore exposes unsafe memory access when used withOffsetArrays
:
julia> using OffsetArraysjulia> sum(OffsetArray([1, 2, 3], -10))9164911648 # inconsistent results or segfault
While the original source of the error here is1:length(A)
, the use of@inbounds
increases the consequences from a bounds error to a less easily caught and debugged unsafe memory access. It is often difficult or impossible to prove that a method which uses@inbounds
is safe, so one must weigh the benefits of performance improvements against the risk of segfaults and silent misbehavior, especially in public facing APIs.
There may be certain scenarios where for code-organization reasons you want more than one layer between the@inbounds
and@boundscheck
declarations. For instance, the defaultgetindex
methods have the chaingetindex(A::AbstractArray, i::Real)
callsgetindex(IndexStyle(A), A, i)
calls_getindex(::IndexLinear, A, i)
.
To override the "one layer of inlining" rule, a function may be marked withBase.@propagate_inbounds
to propagate an inbounds context (or out of bounds context) through one additional layer of inlining.
The overall hierarchy is:
checkbounds(A, I...)
which calls
checkbounds(Bool, A, I...)
which calls
checkbounds_indices(Bool, axes(A), I)
which recursively calls
checkindex
for each dimensionHereA
is the array, andI
contains the "requested" indices.axes(A)
returns a tuple of "permitted" indices ofA
.
checkbounds(A, I...)
throws an error if the indices are invalid, whereascheckbounds(Bool, A, I...)
returnsfalse
in that circumstance.checkbounds_indices
discards any information about the array other than itsaxes
tuple, and performs a pure indices-vs-indices comparison: this allows relatively few compiled methods to serve a huge variety of array types. Indices are specified as tuples, and are usually compared in a 1-1 fashion with individual dimensions handled by calling another important function,checkindex
: typically,
checkbounds_indices(Bool, (IA1, IA...), (I1, I...)) = checkindex(Bool, IA1, I1) & checkbounds_indices(Bool, IA, I)
socheckindex
checks a single dimension. All of these functions, including the unexportedcheckbounds_indices
have docstrings accessible with?
.
If you have to customize bounds checking for a specific array type, you should specializecheckbounds(Bool, A, I...)
. However, in most cases you should be able to rely oncheckbounds_indices
as long as you supply usefulaxes
for your array type.
If you have novel index types, first consider specializingcheckindex
, which handles a single index for a particular dimension of an array. If you have a custom multidimensional index type (similar toCartesianIndex
), then you may have to consider specializingcheckbounds_indices
.
Note this hierarchy has been designed to reduce the likelihood of method ambiguities. We try to makecheckbounds
the place to specialize on array type, and try to avoid specializations on index types; conversely,checkindex
is intended to be specialized only on index type (especially, the last argument).
Julia can be launched with--check-bounds={yes|no|auto}
to emit bounds checks always, never, or respect@inbounds
declarations.
Settings
This document was generated withDocumenter.jl version 1.8.0 onWednesday 9 July 2025. Using Julia version 1.11.6.