# Doubt about overloading

**URL:** <https://forum.crystal-lang.org/t/doubt-about-overloading/8774>\
**Category:** Help & Support\
**Created:** [March 11, 2026, 6:34am UTC](https://forum.crystal-lang.org/t/doubt-about-overloading/8774 "2026-03-11T06:34:54Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![aiac](https://avatars.discourse-cdn.com/v4/letter/a/db5fbb/32.png) [@aiac](https://forum.crystal-lang.org/u/aiac)\
**Post date:** [March 11, 2026, 6:34am UTC](https://forum.crystal-lang.org/t/doubt-about-overloading/8774/1 "2026-03-11T06:34:54Z")

</div>

```crystal
def foo(x : String)

    puts "foo(String): #{x}."

end

def foo(x : Int32)

    puts "foo(Int32): #{x}."

end

def test(x : Object)

    p! typeof(x) # String | Int32, why not Object?

    foo(x) # compiled, dispatch in run-time?

end

x = ARGV[0]

x = x.to_i32? || x

test(x)

```

```auto
$ ./foo.cr sdfs

typeof(x) # => (Int32 | String)

foo(String): sdfs.

$ ./foo.cr 11

typeof(x) # => (Int32 | String)

foo(Int32): 11.

```

---

<div class="post-metadata">

**Author:** ![kojix2](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/kojix2/32/1583_2.png) [@kojix2](https://forum.crystal-lang.org/u/kojix2)\
**Post date:** [March 11, 2026, 8:16am UTC](https://forum.crystal-lang.org/t/doubt-about-overloading/8774/2 "2026-03-11T08:16:29Z")

</div>

As expected, the decision is made at runtime. A union type has a memory layout consisting of an Int32 type ID and a payload area. The compiled code checks the type ID with conditional branches and calls the corresponding function.

---

<div class="post-metadata">

**Author:** ![konovod](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/konovod/32/229_2.png) [@konovod](https://forum.crystal-lang.org/u/konovod)\
**Post date:** [March 11, 2026, 10:02am UTC](https://forum.crystal-lang.org/t/doubt-about-overloading/8774/3 "2026-03-11T10:02:44Z")

</div>

> [@aiac](#):
>
> `String | Int32, why not Object?`

This is because `Object` is just a restriction, actual types of parameters are calculated during compile time. If you add `test(1.0)` to your example, compiler will complain.

---

<div class="post-metadata">

**Author:** ![HertzDevil](https://yyz2.discourse-cdn.com/flex036/user_avatar/forum.crystal-lang.org/hertzdevil/32/1023_2.png) [@HertzDevil](https://forum.crystal-lang.org/u/HertzDevil)\
**Post date:** [March 11, 2026, 10:47am UTC](https://forum.crystal-lang.org/t/doubt-about-overloading/8774/4 "2026-03-11T10:47:24Z")

</div>

`typeof(x)` is the static type of the parameter, which in turn is the static type of the argument to the `test(x)` call. It comes from`x = x.to_i32? || x`, which can only be an `Int32` or a `String`. The compiler would generate an instance of `test` that has `Int32 | String` parameters; if you separately call `test(11)` and `test("sdfs")` in your source code, there would be extra instantiations for `Int32` and `String` parameters respectively. That is to say, the arguments are never upcast to `Object` (which is unimplemented anyway).

If you know C++, a general rule is that something like this:

```crystal
def foo(x : Int32 | String)
  # typeof(x) may be `Int32`, `String`, or `Int32 | String`
end

```

is analogous to:

```cpp
void foo(std::convertible_to<std::variant<int, const char *>> auto x) {
  // `decltype(x)` may be `int`, `const char *`, or many others due to implicit conversion rules
}

```

---

<div class="post-metadata">

**Author:** ![aiac](https://avatars.discourse-cdn.com/v4/letter/a/db5fbb/32.png) [@aiac](https://forum.crystal-lang.org/u/aiac)\
**Post date:** [March 11, 2026, 11:50am UTC](https://forum.crystal-lang.org/t/doubt-about-overloading/8774/5 "2026-03-11T11:50:36Z")

</div>

That is interesting

```crystal
# priority: Int32|String = String|Int32 > String|Int > String|Int32||Float32 > Object > T > auto

def test(x : Object)

    puts "test(Object)"

    p! typeof(x) # String | Int32

    foo(x) # compiled, dispatch in run-time

end

def test(x : Int32 | String)

    puts "test(Int32|String)"

    p! typeof(x)

    foo(x)

end

# same as test(Int32|String) and would override it

def test(x : String | Int32)

    puts "test(String|Int32)"

    p! typeof(x)

    foo(x)

end

def test(x : String | Int)

    puts "test(String|Int)"

     p! typeof(x)

     foo(x)

end

def test(x : String | Int32 | Float32)

    puts "test(String|Int32|Float32)"

    p! typeof(x)

    foo(x)

end

def test(x : T) forall T

    puts "test(T)"

    p! typeof(x)

    foo(x)

end

def test(x)

    puts "test(x)"

    p! typeof(x)

    foo(x)

end

```

---

<div class="post-metadata">

**Author:** ![aiac](https://avatars.discourse-cdn.com/v4/letter/a/db5fbb/32.png) [@aiac](https://forum.crystal-lang.org/u/aiac)\
**Post date:** [March 11, 2026, 12:06pm UTC](https://forum.crystal-lang.org/t/doubt-about-overloading/8774/6 "2026-03-11T12:06:15Z")

</div>

I mistakenly believe that crystal, like some other languages, overloading is always in compile-time.
