colors::Vector{Colorant}
means that each element of colors
may be a different subtype of Colorant
. Internally, therefore, it is an array of pointers to “boxes” (values with a type tag), a different box for each element. (Operations on such an array are consequently rather slow because the compiled code needs to look up the type of each element at runtime.) Vector{Colorant}
is one specific concrete type, and no other type is allowed.
In contrast a parameter colors::AbstractVector{<:Colorant}
is declaring that the argument can be any one of a set of types V{T}
, for any V <: AbstractVector
and T <: Colorant
. A Vector{Colorant}
is allowed, but so is a Vector{RGB{Float64}}
, for example. For any particular type of colors
that is passed, the compiler will generate a different specialized implementation (one for each type). For example, a colors::Vector{RGB{Float64}}
is stored as an array of “inlined” (r,g,b)
triplets (3 Float64
values in a row for each color) with no pointers or type tags for individual elements, and since the Colorant
type is known at compile time the generated code can be quite efficient (as efficient as operating in C on an array of struct { double r; double g; double b; }
values).
See also the discussion here, for example, and the Julia manual on Parametric Types.