On Julia 1.10, the following AbstractVector
implementation supports push!
and append!
:
julia> begin
struct MyVector <: AbstractVector{Int}
v::Vector{Int}
function MyVector(args...)
new(Vector{Int}(args...))
end
end
Base.size(mv::MyVector) = size(mv.v)
Base.getindex(v::MyVector, i::Int) = getindex(v.v, i)
Base.setindex!(mv::MyVector, v, i::Int) = mv.v[i] = v
Base.resize!(mv::MyVector, nl::Integer) = resize!(mv.v, nl)
end
julia> v = MyVector(undef, 5)
5-element MyVector:
0
0
0
0
0
julia> push!(v, 6)
6-element MyVector:
0
0
0
0
0
6
julia> append!(v, [1,2,3])
9-element MyVector:
0
0
0
0
0
6
1
2
3
Beyond the AbstractArray
interface, the only other method needed was resize!
. On Julia 1.11, this appears to be no longer the case:
julia> begin
struct MyVector <: AbstractVector{Int}
v::Vector{Int}
function MyVector(args...)
new(Vector{Int}(args...))
end
end
Base.size(mv::MyVector) = size(mv.v)
Base.getindex(v::MyVector, i::Int) = getindex(v.v, i)
Base.setindex!(mv::MyVector, v, i::Int) = mv.v[i] = v
Base.resize!(mv::MyVector, nl::Integer) = resize!(mv.v, nl)
end
julia> v = MyVector(undef, 5)
5-element MyVector:
0
0
0
0
0
julia> push!(v, 6)
ERROR: MethodError: no method matching sizehint!(::MyVector, ::Int64)
The function `sizehint!` exists, but no method is defined for this combination of argument types.
Closest candidates are:
sizehint!(::BitSet, ::Integer; first, shrink)
@ Base bitset.jl:58
sizehint!(::BitVector, ::Integer)
@ Base bitarray.jl:809
sizehint!(::Dict{T}, ::Any; shrink) where T
@ Base dict.jl:193
...
Stacktrace:
[1] _append!(a::MyVector, ::Base.HasLength, iter::Tuple{Int64})
@ Base ./array.jl:1320
[2] append!(a::MyVector, iter::Tuple{Int64})
@ Base ./array.jl:1313
[3] push!(a::MyVector, iter::Int64)
@ Base ./array.jl:1314
[4] top-level scope
@ REPL[3]:1
If I follow the suggestion and implement sizehint!
, I then get a stack overflow.
julia> Base.sizehint!(v::MyVector, nl::Integer) = sizehint!(v.v, nl)
julia> push!(v, 6)
ERROR: StackOverflowError:
Stacktrace:
[1] sizehint!(a::Vector{Int64}, sz::Int64; first::Bool, shrink::Bool)
@ Base ./array.jl:1480
[2] sizehint!
@ ./array.jl:1480 [inlined]
[3] sizehint!
@ ./REPL[4]:1 [inlined]
[4] sizehint!
@ ./array.jl:1519 [inlined]
[5] _append!
@ ./array.jl:1320 [inlined]
[6] append!
@ ./array.jl:1313 [inlined]
[7] push!(a::MyVector, iter::Int64)
@ Base ./array.jl:1314
[8] _append!
@ ./array.jl:1322 [inlined]
--- the above 3 lines are repeated 79981 more times ---
[239952] append!
@ ./array.jl:1313 [inlined]
The stack overflow is due to push!
calling append!
which in turn calls push!
. This appears to be due to the following pull request that is in Julia 1.11.0-rc2:
I discovered this while I was doing some maintenance on UInt12Arrays.jl, and I noticed that my tests were failing on Julia 1.11.0-rc2. There I noticed that push!
and append!
no longer work like above. I wonder if this might affect other AbstractArray
or AbstractVector
implementations.
Perhaps resize!
and sizehint!
should be added to the AbstractArray
interface as an optional method to enable push!
and append!
.
If we only wanted to depend on resize!
for push!
and append!
to work, perhaps we should consider adding the following to Base
:
import Base: push!, sizehint!
function push!(vec::AbstractVector, v)
new_length = length(vec) + 1
resize!(vec, new_length)
vec[new_length] = v
return vec
end
# do nothing
sizehint!(vec::AbstractVector, nl::Integer) = vec