# Union type in struct: type conversion

**URL:** <https://discourse.julialang.org/t/union-type-in-struct-type-conversion/42276>\
**Category:** New to Julia\
**Created:** [June 29, 2020, 10:34pm UTC](https://discourse.julialang.org/t/union-type-in-struct-type-conversion/42276 "2020-06-29T22:34:58Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![Egwene\_al\_Vere](https://avatars.discourse-cdn.com/v4/letter/e/df788c/32.png) [@Egwene\_al\_Vere](https://discourse.julialang.org/u/Egwene_al_Vere)\
**Post date:** [June 29, 2020, 10:34pm UTC](https://discourse.julialang.org/t/union-type-in-struct-type-conversion/42276/1 "2020-06-29T22:34:58Z")

</div>

As a minimum example, one field in my struct can be a complex-valued matrix or vector:

```julia
struct foo_1
    a::VecOrMat{ComplexF64}
end

VecOrMat{ComplexF64}(x::Vector) = Vector{ComplexF64}(x)
VecOrMat{ComplexF64}(x::Matrix) = Matrix{ComplexF64}(x)

```

which allows the input to be, e.g. an integer vector, and convert it to complex floating vector:

```julia
foo_1([1, 2])

```

> foo\_1(Complex{Float64}[1.0 + 0.0im, 2.0 + 0.0im])

This works as expected; however, another field in my struct can be a floating-point number or vector of such:

```julia
struct foo_2
    x::Union{Float64, Vector{Float64}}
end

Union{Float64, Vector{Float64}}(x::Number) = Float64(x)
Union{Float64, Vector{Float64}}(x::Vector) = Vector{Float64}(x)

```

This however errors:

```julia
foo_2(1)

```

> MethodError: Cannot `convert` an object of type  
> Int64 to an object of type  
> Union{Float64, Array{Float64,1}}  
> Closest candidates are:  
> convert(::Type{T}, !Matched::T) where T at essentials.jl:171  
> Union{Float64, Array{Float64,1}}(::Number) at In[3]:5
> 
> Stacktrace:  
> [1] foo\_2(::Int64) at ./In[3]:2  
> [2] top-level scope at In[6]:1

given `VecOrMat` is just `Union{Array{T,1}, Array{T,2}} where T`, why is be behavior of `foo_2` different from `foo_1`? `@code_typed` shows

```julia
@code_typed foo_1([1, 2])

```

```julia
CodeInfo(
1 ─ %1 = Main.foo_1::Core.Compiler.Const(foo_1, false)
│ %2 = Core.fieldtype(%1, 1)::Type{Union{Array{Complex{Float64},1}, Array{Complex{Float64},2}}}
│ %3 = invoke Base.convert(%2::Type{Union{Array{Complex{Float64},1}, Array{Complex{Float64},2}}}, _2::Array{Int64,1})::Any
│ %4 = %new(%1, %3)::foo_1
└── return %4
) => foo_1

```

and

```julia
@code_typed foo_2([1, 2])

```

```julia
CodeInfo(
1 ─ %1 = Main.foo_2::Core.Compiler.Const(foo_2, false)
│ %2 = Core.fieldtype(%1, 1)::Type{Union{Float64, Array{Float64,1}}}
│ Base.convert(%2, x)::Union{}
└── $(Expr(:unreachable))::Union{}
) => Union{}

```

I could overload `Base.convert()` to make `foo_2` work,

```julia
import Base.convert
convert(::Type{Union{Float64, Vector{Float64}}}, x::Number) = Float64(x)
convert(::Type{Union{Float64, Vector{Float64}}}, x::Vector) = Vector{Float64}(x)

```

but still wondering the reason why the error happens, and what’s a better way to deal with this error? Thanks!

---

<div class="post-metadata">

**Author:** ![dpsanders](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dpsanders/32/3573_2.png) [@dpsanders](https://discourse.julialang.org/u/dpsanders)\
**Post date:** [June 30, 2020, 12:35am UTC](https://discourse.julialang.org/t/union-type-in-struct-type-conversion/42276/2 "2020-06-30T00:35:44Z")

</div>

> [@Egwene\_al\_Vere](#):
>
> ```julia
> struct foo_1
> a::VecOrMat{ComplexF64}
> end
> 
> ```

You should make a parametric type.

(And the convention is to have type names that start with a capital letter.)

```julia
struct Foo1{T <: VecOrMat{ComplexF64}}
    a::T
end

```

---

<div class="post-metadata">

**Author:** ![tim.holy](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tim.holy/32/52_2.png) [@tim.holy](https://discourse.julialang.org/u/tim.holy)\
**Post date:** [June 30, 2020, 10:05pm UTC](https://discourse.julialang.org/t/union-type-in-struct-type-conversion/42276/3 "2020-06-30T22:05:41Z")

</div>

@dpsanders recommendation is a good one if you want to specialize based on which type is in the field. There are a few cases where you _don’t_ want to do that, so if you want to continue with your design then `@code_typed` gives you a clue how to fix it:

```julia
julia> @code_typed foo_1([1, 2])
CodeInfo(
1 ─ %1 = Main.foo_1::Core.Compiler.Const(foo_1, false)
│ %2 = invoke Union{Array{Complex{Float64},1}, Array{Complex{Float64},2}}(_2::Array{Int64,1})::Any
│ %3 = %new(%1, %2)::foo_1
└── return %3
) => foo_1

```

You can see that the `invoke` is calling the methods you defined.

In contrast,

```julia
julia> @code_typed foo_2(1)
CodeInfo(
1 ─ %1 = Main.foo_2::Core.Compiler.Const(foo_2, false)
│ %2 = Core.fieldtype(%1, 1)::Type{Union{Float64, Array{Float64,1}}}
│ Base.convert(%2, x)::Union{}
└── unreachable
) => Union{}

```

calls `Base.convert`. So you should define the `convert` method in addition to/instead of the ones you defined:

```julia
julia> Base.convert(::Type{Union{Float64, Vector{Float64}}}, x::Number) = Float64(x)

julia> Base.convert(::Type{Union{Float64, Vector{Float64}}}, x::Vector) = Vector{Float64}(x)

julia> foo_2(1)
foo_2(1.0)

```

To be honest I’m not sure why these two differ.

---

<div class="post-metadata">

**Author:** ![ChadFulton](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chadfulton/32/27840_2.png) [@ChadFulton](https://discourse.julialang.org/u/ChadFulton)\
**Post date:** [August 4, 2021, 2:35pm UTC](https://discourse.julialang.org/t/union-type-in-struct-type-conversion/42276/4 "2021-08-04T14:35:05Z")

</div>

> So you should define the `convert` method in addition to/instead of the ones you defined:
> 
> ```julia
> julia> Base.convert(::Type{Union{Float64, Vector{Float64}}}, x::Number) = Float64(x)
> julia> Base.convert(::Type{Union{Float64, Vector{Float64}}}, x::Vector) = Vector{Float64}(x)
> 
> ```

Sorry to revive this thread, but is there any downside to overriding Base.convert in this way? These particular definitions seem benign, but e.g. [this Github issue](https://github.com/JuliaLang/julia/issues/29321) seems to suggest that overriding Base.convert is a bad idea (“type piracy”).

If overriding Base.convert is a bad idea, is there another way to go about this?

---

<div class="post-metadata">

**Author:** ![ChadFulton](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chadfulton/32/27840_2.png) [@ChadFulton](https://discourse.julialang.org/u/ChadFulton)\
**Post date:** [August 4, 2021, 3:59pm UTC](https://discourse.julialang.org/t/union-type-in-struct-type-conversion/42276/5 "2021-08-04T15:59:59Z")

</div>

A related question is about the code suggested as working by OP:

```julia
VecOrMat{ComplexF64}(x::Vector) = Vector{ComplexF64}(x)
VecOrMat{ComplexF64}(x::Matrix) = Matrix{ComplexF64}(x)

```

is this considered type piracy, since it modifies the behavior of `VecOrMat`?
