# Broadcasting over an array of custom struct gives type instability

**URL:** <https://discourse.julialang.org/t/broadcasting-over-an-array-of-custom-struct-gives-type-instability/131086>\
**Category:** General Usage\
**Tags:** struct, type-stability\
**Created:** [July 27, 2025, 4:38am UTC](https://discourse.julialang.org/t/broadcasting-over-an-array-of-custom-struct-gives-type-instability/131086 "2025-07-27T04:38:01Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![albertomercurio](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/albertomercurio/32/27051_2.png) [@albertomercurio](https://discourse.julialang.org/u/albertomercurio)\
**Post date:** [July 27, 2025, 4:38am UTC](https://discourse.julialang.org/t/broadcasting-over-an-array-of-custom-struct-gives-type-instability/131086/1 "2025-07-27T04:38:01Z")

</div>

Hello,

I have a simple problem in Julia v1.10

```julia-auto
using Test

struct MyMatrix{MT<:AbstractMatrix}
    M::MT
end

Base.:(*)(A::MyMatrix, x::Number) = MyMatrix(A.M * x)

function f(x)
    # return [y / 10 for y in x]
    return x * 10
end

# states = [rand_dm(10) for _ in 1:10]
states = [MyMatrix(rand(10, 10)) for _ in 1:10]

f(states)

@inferred f(states)

```

It detects type instability, since it is using broadcasting. But as soon as I use the comprehension, then everything works. Why?

Notice that the operation `*` itself is type-stable.

Notice also that the matrix can be any `AbstractMatrix` sparse, GPU, etc.

---

<div class="post-metadata">

**Author:** ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)\
**Post date:** [July 27, 2025, 6:56am UTC](https://discourse.julialang.org/t/broadcasting-over-an-array-of-custom-struct-gives-type-instability/131086/2 "2025-07-27T06:56:19Z")

</div>

I’ll leave the analysis of the vector times number method to someone else but if you instead use an explicit broadcast this problem goes away:

```julia-auto
f(x) = x .* 10

```

---

<div class="post-metadata">

**Author:** ![GunnarFarneback](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/gunnarfarneback/32/1827_2.png) [@GunnarFarneback](https://discourse.julialang.org/u/GunnarFarneback)\
**Post date:** [July 27, 2025, 8:24am UTC](https://discourse.julialang.org/t/broadcasting-over-an-array-of-custom-struct-gives-type-instability/131086/4 "2025-07-27T08:24:29Z")

</div>

I cannot see that using a global const or a local scope makes any difference, neither on 1.10 nor on nightly. It also doesn’t make much sense that it would; the inference is made for the type present when the call is done. It’s something completely different to refer to a non-const global from inside a function, rather than a function argument, which is a common cause of type instability.

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [July 27, 2025, 8:53am UTC](https://discourse.julialang.org/t/broadcasting-over-an-array-of-custom-struct-gives-type-instability/131086/5 "2025-07-27T08:53:25Z")

</div>

The indirect broadcast gets bad inference at the `Base.broadcast_preserving_zero_d` call, I don’t know how `@code_warntype f(states)` misses it:

```julia-auto
julia> @inferred f(states)
ERROR: return type Vector{MyMatrix{Matrix{Float64}}} does not match inferred return type Any
Stacktrace:
 [1] error(s::String)
   @ Base .\error.jl:35
 [2] top-level scope
   @ REPL[68]:1

julia> @code_warntype f(states)
MethodInstance for f(::Vector{MyMatrix{Matrix{Float64}}})
  from f(x) @ Main REPL[50]:1
Arguments
  #self#::Core.Const(Main.f)
  x::Vector{MyMatrix{Matrix{Float64}}}
Body::Vector{MyMatrix{Matrix{Float64}}}
1 ─ %1 = Main.:*::Core.Const(*)
│ %2 = (%1)(x, 10)::Vector{MyMatrix{Matrix{Float64}}}
└── return %2

julia> @code_warntype *(states, 10)
MethodInstance for *(::Vector{MyMatrix{Matrix{Float64}}}, ::Int64)
  from *(A::AbstractArray, B::Number) @ Base arraymath.jl:24
Arguments
  #self#::Core.Const(*)
  A::Vector{MyMatrix{Matrix{Float64}}}
  B::Int64
Body::Any
1 ─ %1 = Base.broadcast_preserving_zero_d::Core.Const(Base.Broadcast.broadcast_preserving_zero_d)
│ %2 = (%1)(Base.:*, A, B)::Any
└── return %2

```

---

<div class="post-metadata">

**Author:** ![\_bernhard](https://avatars.discourse-cdn.com/v4/letter/_/bc79bd/32.png) [@\_bernhard](https://discourse.julialang.org/u/_bernhard)\
**Post date:** [July 27, 2025, 8:59am UTC](https://discourse.julialang.org/t/broadcasting-over-an-array-of-custom-struct-gives-type-instability/131086/6 "2025-07-27T08:59:40Z")

</div>

Thanks for correcting.  
It seems the issue goes away by doing some things, which I wrongly attributed to the introduction of the local scope.  
E.g. re-executing the struct definition makes this issue vanish.

```julia-auto
julia> @inferred f(states);
ERROR: return type Vector{MyMatrix{Matrix{Float64}}} does not match inferred return type Any
Stacktrace:
 [1] error(s::String)
   @ Base ./error.jl:35
 [2] top-level scope
   @ REPL[7]:1

julia> struct MyMatrix{MT<:AbstractMatrix}
           M::MT
       end

julia> @inferred f(states);

```

---

<div class="post-metadata">

**Author:** ![Benny](https://avatars.discourse-cdn.com/v4/letter/b/49beb7/32.png) [@Benny](https://discourse.julialang.org/u/Benny)\
**Post date:** [July 27, 2025, 9:02am UTC](https://discourse.julialang.org/t/broadcasting-over-an-array-of-custom-struct-gives-type-instability/131086/7 "2025-07-27T09:02:52Z")

</div>

> [@\_bernhard](#):
>
> re-executing the struct definition makes this issue vanish.

It also makes `@code_warntype *(states, 10)` type-stable and match `@code_warntype f(states)`, what on earth?

Might be related to

- [Mysterious inference failure that can be solved by redefining function · Issue #35537 · JuliaLang/julia](https://github.com/JuliaLang/julia/issues/35537)
- [problem with the unreliable approximation of `Core.Compiler.return_type` · Issue #35800 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/35800#issuecomment-1097605769)

because re-evaluating struct definitions with no changes currently keep the type but reevaluates the inner and default constructors.
