copy(x::CartesianIndices) and copy(x::LinearIndices) do not return the same type as the input argument.
I guess you could argue that it doesn’t make sense to copy an immutable. But perhaps you could also argue that trying to copy an immutable should either return the correct type or result in a “no matching method” error.
There is a precedent for defining copy for immutables. Here’s a definition for copying ranges from Base:
# Ranges are immutable
copy(r::AbstractRange) = r
So let’s try copying CartesianIndices and LinearIndices
dims = ntuple(d -> -1:1, 2)
CIs = CartesianIndices(dims)
LIs = LinearIndices(CIs)
julia> copy(CIs)
3×3 Array{CartesianIndex{2},2}:
CartesianIndex(-1, -1) CartesianIndex(-1, 0) CartesianIndex(-1, 1)
CartesianIndex(0, -1) CartesianIndex(0, 0) CartesianIndex(0, 1)
CartesianIndex(1, -1) CartesianIndex(1, 0) CartesianIndex(1, 1)
julia> copy(LIs)
3×3 Array{Int64,2}:
1 4 7
2 5 8
3 6 9
The fallback for copy seems to go through similar which returns an Array for these types.
For non-standard indexing, an OffsetArray is returned (if OffsetArrays are in scope, otherwise there’s a no method matching error).
Summary
dims = ntuple(d -> -1:1, 2)
CIs = CartesianIndices(Base.IdentityUnitRange.(dims))
LIs = LinearIndices(CIs)
julia> copy(CIs)
ERROR: MethodError: no method matching similar(::CartesianIndices{2,Tuple{Base.IdentityUnitRange{UnitRange{Int64}},Base.IdentityUnitRange{UnitRange{Int64}}}}, ::Type{CartesianIndex{2}}, ::Tuple{Base.IdentityUnitRange{UnitRange{Int64}},Base.IdentityUnitRange{UnitRange{Int64}}})
Closest candidates are:
similar(::AbstractArray, ::Type{T}) where T at abstractarray.jl:629
similar(::AbstractArray, ::Type{T}, ::Union{Integer, AbstractUnitRange}...) where T at abstractarray.jl:632
similar(::AbstractArray, ::Type{T}, ::Tuple{Vararg{Int64,N}}) where {T, N} at abstractarray.jl:640
...
Stacktrace:
[1] similar(::CartesianIndices{2,Tuple{Base.IdentityUnitRange{UnitRange{Int64}},Base.IdentityUnitRange{UnitRange{Int64}}}}, ::Type{CartesianIndex{2}}) at .\abstractarray.jl:629
[2] similar(::CartesianIndices{2,Tuple{Base.IdentityUnitRange{UnitRange{Int64}},Base.IdentityUnitRange{UnitRange{Int64}}}}) at .\abstractarray.jl:628
[3] copymutable at .\abstractarray.jl:971 [inlined]
[4] copy(::CartesianIndices{2,Tuple{Base.IdentityUnitRange{UnitRange{Int64}},Base.IdentityUnitRange{UnitRange{Int64}}}}) at .\abstractarray.jl:915
[5] top-level scope at REPL[4]:1
julia> copy(LIs)
ERROR: MethodError: no method matching similar(::LinearIndices{2,Tuple{Base.IdentityUnitRange{UnitRange{Int64}},Base.IdentityUnitRange{UnitRange{Int64}}}}, ::Type{Int64}, ::Tuple{Base.IdentityUnitRange{UnitRange{Int64}},Base.IdentityUnitRange{UnitRange{Int64}}})
Closest candidates are:
similar(::AbstractArray, ::Type{T}) where T at abstractarray.jl:629
similar(::AbstractArray, ::Type{T}, ::Union{Integer, AbstractUnitRange}...) where T at abstractarray.jl:632
similar(::AbstractArray, ::Type{T}, ::Tuple{Vararg{Int64,N}}) where {T, N} at abstractarray.jl:640
...
Stacktrace:
[1] similar(::LinearIndices{2,Tuple{Base.IdentityUnitRange{UnitRange{Int64}},Base.IdentityUnitRange{UnitRange{Int64}}}}, ::Type{Int64}) at .\abstractarray.jl:629
[2] similar(::LinearIndices{2,Tuple{Base.IdentityUnitRange{UnitRange{Int64}},Base.IdentityUnitRange{UnitRange{Int64}}}}) at .\abstractarray.jl:628
[3] copymutable at .\abstractarray.jl:971 [inlined]
[4] copy(::LinearIndices{2,Tuple{Base.IdentityUnitRange{UnitRange{Int64}},Base.IdentityUnitRange{UnitRange{Int64}}}}) at .\abstractarray.jl:915
[5] top-level scope at REPL[5]:1
Would it make sense to define the following copy methods?
Base.copy(x::CartesianIndices) = x
Base.copy(x::LinearIndices) = x