---
title: Crystal 1.3.0 is released!
version: 1.3.0
summary:
thumbnail: +
author:
---

We are delivering a new release with several bugfixes and improvements. Below we list the most important or interesting changes, without mentioning several bugfixes and smaller enhancements. For more details, visit the [changelog](https://github.com/crystal-lang/crystal/releases/tag/1.3.0). Breaking changes are marked with ⚠️.

Pre-built packages are available on [GitHub Releases](https://github.com/crystal-lang/crystal/releases/tag/1.3.0) and our official distribution channels.
See [crystal-lang.org/install](https://crystal-lang.org/install/) for installation instructions.

## Stats

In this release we included [176 changes since the 1.2.2 release](https://github.com/crystal-lang/crystal/pulls?q=is%3Apr+milestone%3A1.3.0) by 31 contributors. We thank all the effort put into improving the language! ❤️

## Interpreter

The awaited Crystal interpreter has been [merged](https://github.com/crystal-lang/crystal/pull/11159). But it's still a preview and [misses substantial features](https://github.com/crystal-lang/crystal/issues/11555) for real use cases.

Our distribution packages are **not** being compiled with it, and we recommend the same for external packages. If you want to try it, you need to build the compiler with interpreter support explicitly (`make crystal interpreter=1`).

You can invoke the interpreter using two modes: `crystal i` or `crystal i file.cr`. In the first case, a REPL will start right away, and in the second case the `file.cr` will be interpreted. Interestingly, if the interpreter finds the `debugger` instruction, it will start an interactive process to debug it. For instance, if we have a file `example.cr` with:

```crystal
a = 1
debugger
p a
```

Then calling `crystal i example.cr` stops after the call to `debugger`:
```text
From: example.cr:3:3 <Program>#example.cr:

    1: a = 1
    2: debugger
 => 3: p a

 pry>
```

At this point we can inspect and modify variables, step into function calls, etc. If we enter `a = 2` in the REPL and then `next`, we'll see the program printing `2`.

Read more about the interpreter in our blog post: [*Crystal's interpreter – A very special holiday present*](https://crystal-lang.org/2021/12/29/crystal-i.html)

## Windows Support

With over [30 individual changes](https://github.com/crystal-lang/crystal/pulls?q=is%3Apr+milestone%3A1.3.0+label%3Aplatform%3Awindows) there has been great progress for improving Windows support.

It's still not yet a fully supported platform, but we've started building self-contained and portable Windows packages.

A self-contained and portable snapshot package for Windows x86-64 is available [on the GitHub release](https://github.com/crystal-lang/crystal/releases/download/1.3.0/crystal-1.3.0-windows-x86_64-msvc-unsupported.zip). Please remember that this is still experimental and unsupported.

We've also started building packages for nightlies, and you can grab the current build at [nightly.link/crystal-lang/crystal/workflows/win/master/crystal.zip](https://nightly.link/crystal-lang/crystal/workflows/win/master/crystal.zip). There is also a repository for the [scoop package manager](https://scoop.sh/) at [neatorobito/scoop-crystal](https://github.com/neatorobito/scoop-crystal).
Together with many smaller improvements in the compiler, user experience on Windows has greatly improved.

One of the most important missing features has been added: Stack traces. Now you can properly follow where your exceptions come from on Windows ([#11461](https://github.com/crystal-lang/crystal/pull/11461)).

Additionally, several stdlib APIs have been ported to Windows, including `Big` numbers ([#11412](https://github.com/crystal-lang/crystal/pull/11412)), `OpenSSL` ([#11477](https://github.com/crystal-lang/crystal/pull/11477)), and text encoding ([#11480](https://github.com/crystal-lang/crystal/pull/11480)).
As a result, 96% of stdlib specs run successfully on Windows now.

## Unicode

There are a couple of changes related to Unicode support.

Most significant is that [`Char#inspect`](https://crystal-lang.org/api/1.3.0/Char.html#inspect%3AString-instance-method) and [`#dump`](https://crystal-lang.org/api/1.3.0/Char.html#dump%3AString-instance-method) as well as [`String#inspect`](https://crystal-lang.org/api/1.3.0/String.html#inspect%3AString-instance-method) and [`#dump`](https://crystal-lang.org/api/1.3.0/String.html#dump%3AString-instance-method) escape all non-printable characters ([#11452](https://github.com/crystal-lang/crystal/pull/11452)). Note that this may break up grapheme clusters if they depend on non-printable characters. There might be further refinements to this, but we believe it's better to be explicit over potentially missing characters ([#11630](https://github.com/crystal-lang/crystal/issues/11630)).

Further, we added an API for Unicode extended grapheme clusters at [`String::Grapheme`](https://crystal-lang.org/api/1.3.0/String/Grapheme.html) ([#11472](https://github.com/crystal-lang/crystal/pull/11472)). It allows splitting a string into grapheme clusters which represent a user-perceived character and may consist of multiple code points.

```crystal
"a👍🏼à".graphemes # => [String::Grapheme('a'), String::Grapheme("👍🏼"), String::Grapheme("à")]
"a👍🏼à".chars     # => ['a', '👍', '🏼', 'a', '̀']

"a👍🏼à".grapheme_size # => 3
"a👍🏼à".size          # => 5
```

This API is experimental for now, and we expect to enhance and refine it in the following releases.

Further notable changes:

* New: [`Char#unicode_escape`](https://crystal-lang.org/api/1.3.0Char.html#unicode_escape%3AString-instance-method) returns the Unicode escape sequence representing the character ([#11421](https://github.com/crystal-lang/crystal/pull/11421)).
* New: [`Char#printable?`](https://crystal-lang.org/api/1.3.0Char.html#printable%3F-instance-method) returns `true` for printable characters, i.e. those with a visible glyph as well as the ASCII whitespace (`U+0020`) ([#11429](https://github.com/crystal-lang/crystal/pull/11429)).
* ⚠️ Fix: [`Char#ascii_control?`](https://crystal-lang.org/api/1.3.0Char.html#ascii_control%3F%3ABool-instance-method) no longer applies to *C1 control codes*. They are not part of the ASCII character set ([#11510](https://github.com/crystal-lang/crystal/pull/11510)).
* Fix: [`Char#letter?`](https://crystal-lang.org/api/1.3.0Char.html#letter%3F%3ABool-instance-method) correctly identifies code points in the Unicode general categories `Lo` (*Other Letter*) and `Lm` (*Modifier Letter*) as letters ([#11474](https://github.com/crystal-lang/crystal/pull/11474)).
* Standardized Unicode escape formats for [`Char#inspect`](https://crystal-lang.org/api/1.3.0Char.html#inspect%3AString-instance-method) and [`#dump`](https://crystal-lang.org/api/1.3.0Char.html#dump%3AString-instance-method) ([#11421](https://github.com/crystal-lang/crystal/pull/11421)).

## Syscalls

We added an experimental API to create native system calls ([#10777](https://github.com/crystal-lang/crystal/pull/10777)). Only Linux is supported for now. This is a first step towards supporting Linux's `io_uring` interface to improve IO performance.

To define system calls open a module and use the [`Syscall.def_syscall`](https://crystal-lang.org/api/1.3.0/Syscall.html#def_syscall%28name%2Creturn_type%2C%2Aargs%29-macro) macro. As demonstrated in the following example, you need to pass in the system call name, the return type and its arguments.

```crystal
require "syscall"

module MySyscalls
  Syscall.def_syscall write, Int32, fd : Int32, buf : UInt8*, count : LibC::SizeT
end

data = "Hello!\n"
MySyscalls.write(1, data.to_unsafe, LibC::SizeT.new(data.size))
```

## Multi-Assign

Multiple assignments got improved in various ways. First, it's possible to use a splat target in a multi-assignment ([#10410](https://github.com/crystal-lang/crystal/pull/10410)):

```crystal
# Splat in multi-assign with array
first, *rest, last = [1, 2, 3, 4, 5]
first # => 1
rest # => [2, 3, 4]
last # => 5
```

```crystal
# Splat in multi-assign with tuple
*rest, last = {"This", 15, 4, "tuple", true}
rest # => {"This", 15, 4, "tuple"}
last # => true
```

To take the first and last element, it is possible to use the _underscore splat_ notation:
```crystal
# Ignoring the elements in the middle
first, *_, last = {"This", 15, 4, "tuple", true}
first # => "This"
last # => true
```

Second, there's an optional preview feature to detect unbalanced multi-assignments ([#11145](https://github.com/crystal-lang/crystal/pull/11145)). It can be enabled with the compiler flag `-Dstrict_multi_assign`.
```crystal
a, b, c = {1, 2} # Error: index out of bounds for Tuple(Int32, Int32) (2 not in -2..1)
a, b = {1, 2, 3} # Error: cannot assign Tuple(Int32, Int32, Int32) to 2 targets
```

Note that the first example fails with a similar error in 1.2.2 (this error is [expected to change](https://github.com/crystal-lang/crystal/issues/11695) in the near future).

To ignore the extra items in strict mode you can use the underscore splat notation. This can be used to port existing code to work with the `strict_multi_assign` flag.
```crystal
a, b, *_ = {1, 2, 3} # Same as a, b = {1, 2, 3} without the flag
```

⚠️ This second improvement is a breaking change; therefore it's not enabled by default. We encourage using it to detect possible errors in your code. This setting will likely be the default in 2.0. An additional restriction guarded by that flag is that if the right-hand side has a unique element, it must be of type `Indexable` ([#11545](https://github.com/crystal-lang/crystal/pull/11545)).

## Number Autocast

Primitive numeric values are now _autocasted_ to fit into larger types ([#11431](https://github.com/crystal-lang/crystal/pull/11431), [#11529](https://github.com/crystal-lang/crystal/pull/11529)). For instance, it is possible to call a function expecting an `Int64` with an `Int32` (note that before 1.3.0 only number _literals_ were autocasted):

```crystal
def foo(x : Int64)
  x
end

foo 1_i32 # Works in 1.2.2 and 1.3.0

bar = 1_i32
foo bar  # Fails in 1.2.2, works in 1.3.0
```

Unsigned integer types can be autocasted into larger signed ones. And autocasting also works for floating point types (`Float32` to `Float64`).

If there is ambiguity, for instance, because there is more than one option, the compiler throws an error:

```crystal
def foo(x : Int64)
  x
end

def foo(x : Int128)
  x
end

bar = 1_i32
foo bar # Error: ambiguous call, implicit cast of Int32 matches all of Int64, Int128
```

## 128-bit Literals

The parser has been improved to understand number literals in the full range or 128-bit integers ([#11571](https://github.com/crystal-lang/crystal/pull/11571)).
Until now, 128-bit literals had been supported only within the limits of 64-bit values.

```crystal
1_i128                                       # Works in 1.2.2 and 1.3.0
170141183460469231731687303715884105727_i128 # Fails in 1.2.2, works in 1.3.0
```

In order to get there, we needed to implement some arithmetic primitives for all platforms, and refactor the parsing of number literals ([#11211](https://github.com/crystal-lang/crystal/pull/11211)).
The latter also cleaned up a couple of edge cases. Some examples are highlighted here:

```crystal
1_.1   # Error: unexpected '_' in number
-0u64  # Error: Invalid negative value -0 for UInt64
-0_u64 # Error: Invalid negative value -0 for UInt64
1__2   # Error: consecutive underscores in numbers aren't allowed
0x_2   # Error: unexpected '_' in number
0_12   # Error: octal constants should be prefixed with 0o
0e40   # => 0.0
0x     # Error: numeric literal without digits
```

## Other notable changes

- ⚠️ Methods that enumerate on a sub-range now always use `start` as parameter name for the begin of the sub-range ([#11350](https://github.com/crystal-lang/crystal/pull/11350)). This was standardized from several different forms, which keep working as deprecated overloads until the next major release.
- ⚠️ More refactoring happened to `Indexable::Mutable#fill`'s overloads ([#11368](https://github.com/crystal-lang/crystal/pull/11368)). Again, existing code continues to work with deprecated overloads.
- ⚠️ `Regex#name_table` returns `Hash(Int32, String)` instead of `Hash(Int16, String)` ([#11539](https://github.com/crystal-lang/crystal/pull/11539)). Noticeable effects are pretty limited due to number autocasting.

> **THANKS:**
> We have been able to do all of this thanks to the continued support of [84codes](https://www.84codes.com/), [Nikola Motor Company](https://nikolamotor.com/) and every other [sponsor](/sponsors). To maintain and increase the development pace, donations and sponsorships are essential. [OpenCollective](https://opencollective.com/crystal-lang) is available for that.
>
> Reach out to [crystal@manas.tech](mailto:crystal@manas.tech) if you’d like to become a direct sponsor or find other ways to support Crystal. We thank you in advance!
