In general, “it depends” - you could incur a lot of overhead (particularly in a hot loop), and you’d be making life harder for the compiler, but the flexibility may be worth it if runtime isn’t critical. The only way to answer the question of performance penalty for your particular application is through benchmarks, but just to give you a rough idea, compare:
julia> abstract type GenericContainer end
julia> struct MyGenericArrayStruct{T} <: GenericContainer
val::Array{T}
end
julia> struct MyGenericVectorStruct{T} <: GenericContainer
val::Array{T,1}
end
julia> total(g::GenericContainer) = sum(g.val)
total (generic function with 1 method)
julia> using BenchmarkTools
julia> mgas = [MyGenericArrayStruct(rand(3)) for i = 1:10^6];
julia> mgvs = [MyGenericVectorStruct(rand(3)) for i = 1:10^6];
julia> @btime mapreduce(total, +, $mgas)
63.524 ms (1999999 allocations: 30.52 MiB)
1.5007591209103481e6
julia> @btime mapreduce(total, +, $mgvs)
6.469 ms (0 allocations: 0 bytes)
1.499610642683319e6