# Strange type constructor quirk

**URL:** <https://discourse.julialang.org/t/strange-type-constructor-quirk/127265>\
**Category:** New to Julia\
**Created:** [March 22, 2025, 6:15pm UTC](https://discourse.julialang.org/t/strange-type-constructor-quirk/127265 "2025-03-22T18:15:43Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![Salmon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/salmon/32/22968_2.png) [@Salmon](https://discourse.julialang.org/u/Salmon)\
**Post date:** [March 22, 2025, 6:15pm UTC](https://discourse.julialang.org/t/strange-type-constructor-quirk/127265/1 "2025-03-22T18:15:43Z")

</div>

\*Edit: changed title to be more accurate  
Hi, everyone. So, I`m not exactly new to julia but this feels like it belongs here as it is quite basic and still puzzles me.

Let’s consider the MWE in following code snippet:

```julia
struct B 
    x::Ref{Union{Nothing,Int}}
    y::Int
end

struct B2{T}
    x::Ref{Union{Nothing,Int}}
    y::T
end

```

So far, so good. Both structs only differ in the fact that `B2` is a parametric type for the second variable, `y`. Here comes the bit that confused me:

```julia

b = B(nothing,1)# works, returns B(Base.RefValue{Union{Nothing, Int64}}(nothing), 1)

b2 = B2(nothing,1)# ERROR: MethodError: no method matching B2(::Nothing, ::Int64)
b2 = B2(convert(Ref{Union{Nothing,Int}},nothing),1) #works, returns B2{Int64}(Base.RefValue{Union{Nothing, Int64}}(nothing), 1)
b2 = B2{Int}(nothing, 1) #works, returns B2{Int64}(Base.RefValue{Union{Nothing, Int64}}(nothing), 1)

```

In the first line, julia seems to realize that I want `b` to hold a Ref to nothing. This seems to be because `nothing` can apparently be converted to the type `Ref{Union{Nothing,Int}}` (somewhat surprising).

However, if I consider the parametric type `B2`, this automatic conversion no longer works (line 2). This surprises me even more, because the type `T` has nothing to do with `x`.

This can be cirumvented by either converting `nothing` explicitly, or providing the type `T` directly.

1. But why does julia not do again what it did in the first example and convert this automagically?
2. Anyway, is this rather unintuitive type conversion something that one can rely on, or could it be that it is unintended behaviour and might change in future minor releases?

Thanks for your insight!

---

<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:** [March 22, 2025, 6:28pm UTC](https://discourse.julialang.org/t/strange-type-constructor-quirk/127265/2 "2025-03-22T18:28:30Z")

</div>

It’s not a matter of type inference, it’s simply because a `B(x, y) = new(x, y)` method (`new` tries to convert `x` and `y` to the annotated field types) was automatically generated while `B2(x, y)` method wasn’t, which you basically showed. This makes it more apparent:

```julia
julia> methods(B)
# 2 methods for type constructor:
 [1] B(x::Ref{Union{Nothing, Int64}}, y::Int64)
     @ REPL[23]:3
 [2] B(x, y)
     @ REPL[23]:3

julia> methods(B2)
# 1 method for type constructor:
 [1] B2(x::Ref{Union{Nothing, Int64}}, y::T) where T
     @ REPL[23]:8

julia> methods(B2.body) # trick to find parametric constructors
# 2 methods for type constructor:
 [1] B2(x::Ref{Union{Nothing, Int64}}, y::T) where T
     @ REPL[23]:8
 [2] (var"#ctor-self#"::Type{B2{T}} where T)(x, y)
     @ REPL[23]:8

```

---

<div class="post-metadata">

**Author:** ![jakobnissen](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jakobnissen/32/13477_2.png) [@jakobnissen](https://discourse.julialang.org/u/jakobnissen)\
**Post date:** [March 22, 2025, 6:28pm UTC](https://discourse.julialang.org/t/strange-type-constructor-quirk/127265/3 "2025-03-22T18:28:44Z")

</div>

This is not an issue with type inference or conversion, it’s about what default constructors gets defined when you define a new type.

The issue is described in more detail in [Autogenerate `(::Type{Foo})(x::T, y) where T` constructor for type Foo{T} · Issue #35053 · JuliaLang/julia · GitHub](https://github.com/JuliaLang/julia/issues/35053)

---

<div class="post-metadata">

**Author:** ![Salmon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/salmon/32/22968_2.png) [@Salmon](https://discourse.julialang.org/u/Salmon)\
**Post date:** [March 22, 2025, 9:13pm UTC](https://discourse.julialang.org/t/strange-type-constructor-quirk/127265/4 "2025-03-22T21:13:04Z")

</div>

Ah, that makes sense thanks for the clarification!  
My title is quite inaccurate indeed.

I suppose it should be safe to rely on the automatic method definition then, looks like if anything is going to change it would be that these methods are also added for functions with parametric types.

---

<div class="post-metadata">

**Author:** ![favba](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/favba/32/2735_2.png) [@favba](https://discourse.julialang.org/u/favba)\
**Post date:** [March 23, 2025, 12:42am UTC](https://discourse.julialang.org/t/strange-type-constructor-quirk/127265/5 "2025-03-23T00:42:45Z")

</div>

This is just a note on a problem I had once when using `Ref`s in a struct and relevant only if you are actually using something like that in your code.

I’m not on my computer so I can’t check if that’s still the case, but at least it used to be that `Ref` is actually an abstract type. You’d want to use `Base.RefValue{Union{Nothing, Int}}` as the type of the `x` field instead.

---

<div class="post-metadata">

**Author:** ![Salmon](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/salmon/32/22968_2.png) [@Salmon](https://discourse.julialang.org/u/Salmon)\
**Post date:** [March 23, 2025, 6:14am UTC](https://discourse.julialang.org/t/strange-type-constructor-quirk/127265/6 "2025-03-23T06:14:10Z")

</div>

Thanks for the info. Youre right, I do want the functionality of RefValue. I have checked and Ref{T} is indeed an abstract type.

---

<div class="post-metadata">

**Author:** ![nsajko](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/nsajko/32/221187_2.png) [@nsajko](https://discourse.julialang.org/u/nsajko)\
**Post date:** [March 23, 2025, 8:23am UTC](https://discourse.julialang.org/t/strange-type-constructor-quirk/127265/7 "2025-03-23T08:23:31Z")

</div>

While it’s true that `Ref` is abstract, `RefValue` is internal to `Base`, an implementation detail not available to users.

---

<div class="post-metadata">

**Author:** ![favba](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/favba/32/2735_2.png) [@favba](https://discourse.julialang.org/u/favba)\
**Post date:** [March 24, 2025, 12:26pm UTC](https://discourse.julialang.org/t/strange-type-constructor-quirk/127265/8 "2025-03-24T12:26:31Z")

</div>

Well, it definitively **is** available to users (meaning we can use it). it is just not guaranteed to keep it that way between any minor version bumps of Julia.

For this particular case, the only drawback is the extra maintenance burden on the developer, who would have to check compatibility with any minor version change of Julia.

But there is a silly workaround that uses only public API and would be preferred:

```julia
const RefNohtingInt = typeof(Ref{Union{Nothing, Int}}())

struct B 
    x::RefNothingInt
    y::Int
end

struct B2{T}
    x::RefNothingInt
    y::T
end

```

But I really don’t like this opaqueness of `Ref`s. It is not obvious anywhere that is an abstract type. From the documentation, one would assume it is a concrete type
