---
title: Crystal 0.28.0 released!
version: 0.28.0
summary: Crystal has a new development release.
thumbnail: +
author: bcardiff
---

[Crystal 0.28.0](https://github.com/crystal-lang/crystal/releases/tag/0.28.0) has been released!

This is a big release that includes some new language constructs, tidying up some existing features, many interesting additions to the std-lib and important changes for the much awaited multi-threading and windows features.

There were [221 commits since 0.27.2](https://github.com/crystal-lang/crystal/compare/0.27.2...0.28.0) by 31 contributors.

Let’s review some of the most relevant changes in this release. But don't miss the rest of the [release changelog](https://github.com/crystal-lang/crystal/releases/tag/0.28.0) which has a lot of valuable information.

# Language changes

## Enums

Enums are usually declared with one line per each member.

```crystal
enum State
  On
  Off
end
```

Sometimes you might want to declare it in one line, and since `;` is basically a new line for the parser you can do:

```crystal
enum State
  On; Off
end
```

In previous versions using spaces or commas were allowed. From now on `;` is the required one. The formatter will migrate commas to `;` in this version since that construct was used somewhat frequently. Read more at [#7607](https://github.com/crystal-lang/crystal/pull/7607) and  [#7618](https://github.com/crystal-lang/crystal/pull/7618).

## Ranges

Sometimes you don’t know where to start or finish. The same happens to a `Range`. Ranges can now be begin-less and end-less ranges.

Let's see some basic constructs for iterating ranges:

```crystal
(3..).each do |x|
  puts x
  break if some_condition
end
```

<p></p>

```crystal
(..3).reverse_each do |x|
  # ... eventually yields 0, -1, -2
end
```

Make sure that they stop at some point, please.

The good stuff comes from its integration in std-lib. Check [#7179](https://github.com/crystal-lang/crystal/pull/7179) to learn all about them. I will leave here some of my favorites:

```crystal
numbers = [1, 10, 3, 4, 5, 8]
numbers.select(6..) # => [10, 8]
numbers[..2] # => [1, 10, 3]
numbers[...2] # => [1, 10]

[1, 2, 3].zip(6..) # => [[1, 6], [2, 7], [3, 8]]

5.clamp(10..) # => 10
```
## offsetof

The expression `offsetof(Type, @ivar)` was introduced to the language in order to return the offset of the `@ivar` within the memory representation of `Type`. One use case for that is working with  [vertex](https://github.com/crystal-lang/crystal/issues/7566#issuecomment-474965810).

Another awesome aspect of this feature is that it went from initial idea to final implementation in only 1 week by the same person. Going from the need to the full implementation of all the aspects in the lexer, parser, compiler, internals, highlighter, formatter is pretty impressive. Read the full story at [#7589](https://github.com/crystal-lang/crystal/pull/7589).

## Macros

Up until now `Type#annotation` was available to get the last annotation of a given kind. In [#7326](https://github.com/crystal-lang/crystal/pull/7326) `Type#annotations` is added to list all annotations.

Another addition is that  `ArrayLiteral#sort_by` was added in [#3947](https://github.com/crystal-lang/crystal/pull/3947).

With these two features, we can do something like:

```crystal
annotation ReviewedBy
end

@[ReviewedBy("Kevin")]
@[ReviewedBy("Stuart")]
@[ReviewedBy("gru")]
class Foo
end

{% raw %}{% for reviewer in Foo.annotations(ReviewedBy).sort_by(&.[0].downcase) %}
  puts {{reviewer[0]}}
{% end %}{% endraw %}
# output
# gru
# Kevin
# Stuart
```

Note that macros and annotations should not be overused. There are many ways to make a program simple and declarative.
# Compiler

## Deprecated definitions

We are introducing some first-class citizen in the language to have a common idiom to mark methods and other constructs as deprecated. The code is able to compile as usual, but the compiler is able to warn the programmer about usages of deprecated definitions.

In this release, these warnings are off by default in order to gather feedback about what would be the best output and workflow for the regular usage.

Given the following program:

```crystal
# main.cr
@[Deprecated("Use `#bar`")]
def foo
  42
end

puts foo
```

You can build the program with the new options `--warnings all|none --error-on-warnings`.

```shell
$ ./bin/crystal main.cr --warnings all --error-on-warnings
Warning in main.cr:6: Deprecated top-level foo. Use `#bar`

puts foo
     ^~~
A total of 1 warnings were found.
```

The warning options are also available in `crystal spec` and its exit status will be a combination of the spec itself and the deprecation check.

When building the docs using `crystal docs` the deprecated definitions will have a badge. Read more at [#7653](https://github.com/crystal-lang/crystal/pull/7653).

## Library lookup

There was some development to simplify how some libraries and static libraries are looked up and therefore can be overridden in case it is needed. An env var `CRYSTAL_LIBRARY_PATH` is now used in the process of determining the location of libraries to link to.

When using `@[Link("awesome", static: true)]`, the first step will be to lookup `libawesome.a` in the paths listed at `CRYSTAL_LIBRARY_PATH`. If nothing is found `awesome` will be looked up using `pkg-config --static`. If still nothing is found, `libawesome.a` will be looked up in `/usr/lib`, `/usr/local/lib`.

Essentially `CRYSTAL_LIBRARY_PATH` has the highest priority in the lookup. But it is also added to the linker for looking up shared libraries.

The compiler package will set a default value of `CRYSTAL_LIBRARY_PATH` to the embedded libraries where `libgc.a` is located. If the default libraries don’t suit you, just prepend the location where the alternative is located to `CRYSTAL_LIBRARY_PATH`.

Read about this change in [#7562](https://github.com/crystal-lang/crystal/pull/7562) and in the new entry on the [docs](https://crystal-lang.org/api/0.28.0/Link.html)
## Semantic fixes

The compiler includes a bunch of fixes and improvements related to how some code constructs were handled.

A `protected initialize` will now define a protected `new` thanks to [#7510](https://github.com/crystal-lang/crystal/pull/7510).

Some corner cases related to method lookup were fixed in [#7537](https://github.com/crystal-lang/crystal/pull/7537), [#7536](https://github.com/crystal-lang/crystal/pull/7536), and [#7529](https://github.com/crystal-lang/crystal/pull/7529). There should be a couple of fewer surprises now.

The type inference was also improved a bit when dealing with procs in [#7527](https://github.com/crystal-lang/crystal/pull/7527) and [#7568](https://github.com/crystal-lang/crystal/pull/7568). If you store procs in structures it might be worth checking out, you might be able to remove a hack or two.

Some error messages were improved to clarify: the scope the user should have in mind thanks to [#7384](https://github.com/crystal-lang/crystal/pull/7384), and some common pitfalls when capturing blocks thanks to [#7406](https://github.com/crystal-lang/crystal/pull/7406).
# Concurrency

We are really happy to include some refactors that move us forward in the way to parallelism and to include an extension to bdwgc that will enable its multi-threading support to work with the Crystal runtime.

Although you can’t use MT right now, the work done at [#7546](https://github.com/crystal-lang/crystal/pull/7546) will enable to compare alternatives in the GC and changes in the scheduler when `-D preview_mt` is used. While the MT is being developed piece by piece we are trying to avoid slow down in the single-threaded apps.

Two notable refactors introduced were cleanups in `Fiber` to extract a `Fiber::StackPool` in [#7417](https://github.com/crystal-lang/crystal/pull/7417), and refactor `IO::Syscall` as `IO::Evented` in [#7505](https://github.com/crystal-lang/crystal/pull/7505).

# Standard library

Given an `Enum` with `@[Flags]` calling `.from_value(0)` or `.from_value?(0)` will now return `None`. Previously it was raising or returning `nil`. This is a breaking-change introduced in [#6516](https://github.com/crystal-lang/crystal/pull/6516) in order to fix the semantics of those methods.

The module `PartialComparable` is deprecated in [#7664](https://github.com/crystal-lang/crystal/pull/7664) since its behaviour has been fully integrated into `Comparable`.
## Numeric

Some time ago we decided to make `Int#/` return `Float`. That will happen in the `0.29.0`. In `0.28.0`, you will need to start changing the code to use `Int#//`. Luckily there compiler can now assist you in that quest. Build your program and specs with `--warnings all`, the usages of `/` will pop-up. We’ve done it in [#7639](https://github.com/crystal-lang/crystal/pull/7639).

Another notable change is that we settle to remove the type suffixes of numbers in [#7525](https://github.com/crystal-lang/crystal/pull/7525). The output will be cleaner now. If, given an expression, you want to know the value, type, and static type you can use `pp!(e, e.class, typeof(e))`.
## Text

Numbers might be used to represent quantities (Seriously!). These can now be printed in a human-readable form using `Number#humanize`, `Int#humanize_bytes` and `Number#format`. This feature was added in [#6314](https://github.com/crystal-lang/crystal/pull/6314).
## Collections

The counterpart of deprecating `PartialComparable` was that a `Comparable#<=>` is now able to return `nil`. This was done in order to allow better support of `Array#sort` over types that don’t have a total ordering. Read more at [#6611](https://github.com/crystal-lang/crystal/pull/6611).

`Iterator#rewind` is removed and `#cycle` got some performance improvements because now the elements are stored in an array and can be cycled despite the nature of the `Iterator`. Read more at [#7440](https://github.com/crystal-lang/crystal/pull/7440).

`Indexable#zip` and `Indexable#zip?` were moved to `Enumerable` and now there are far more flexible. They can work with any number of `Indexable`, `Iterable` or `Iterator`.

```crystal
a = [1, 2, 3]
b = "a".."c"
c = 9.downto(1)

a.zip(b, c) # => [{1, "a", 9}, {2, "b", 8}, {3, "c", 7}]
```

Read more at [#7453](https://github.com/crystal-lang/crystal/pull/7453).
## Time

`Time` got some polishing again but given the impact of the changes we wanted to include, and the new deprecation support we are using that to offer a migration path.

> Don’t forget to build with `--warnings all` your code at least once!

Although you should leave in the present, `Time.now` is now deprecated. But only because you need to know where you are. The code will need to decide between `Time.local` or `Time.utc`, much more explicit. This also affects constructors. `Time.new` is also deprecated and `Time.local` or `Time.utc` can be used to build specific values. Read more at [#5346](https://github.com/crystal-lang/crystal/pull/5346) and  [#7586](https://github.com/crystal-lang/crystal/pull/7586).

`Time#add_span` is renamed and improved in `Time#shift`. It now allows changing a time instance by calendar units and handle other units thanks to [#6598](https://github.com/crystal-lang/crystal/pull/6598).

The last breaking-change in is in `Time#date` that will now return a `Tuple` of `{year, month, day}`. You can keep using `Time#at_beginning_of_day` if a `Time` instance is wanted. Read more at [#5822](https://github.com/crystal-lang/crystal/pull/5822).
## Networking

There were a couple of efforts to improve `HTTP` and `URI`. Although they are breaking-changes there should be easy to migrate.

There are a couple of fixes in `URI` implementation. `URI#opaque` is dropped. `URI#path` no longer returns `Nil`. The `#parse`/`#to_s` normalization and default port handling have changed. Read the full story at [#6323](https://github.com/crystal-lang/crystal/pull/6323).

`HTTP::Status` is introduced to avoid, if wanted, the need to status code numbers. The only breaking change regarding statuses is that `HTTP.default_status_message_for(Int)` was replaced by `HTTP::Status.new(Int).description`. Read more at [#7247](https://github.com/crystal-lang/crystal/pull/7247).

`HTTP::Multipart` was moved to `MIME::Multipart` in [#7085](https://github.com/crystal-lang/crystal/pull/7085).

In OAuth2 errors we no longer expect a proper JSON to exist. Discover why in [#7467](https://github.com/crystal-lang/crystal/pull/7467).

`RequestProcessor` connection reuse logic was fixed to deal properly with unconsumed payloads in [#7055](https://github.com/crystal-lang/crystal/pull/7055).

## Crypto

An `OpenSSL::Algorithm` instead of symbol is now used in `digest`/`hexdigest` to choose the algorithm to use. As a bonus track, LibCrypt's `PKCS5_PBKDF2_HMAC`. Read more at [#7264](https://github.com/crystal-lang/crystal/pull/7264).

# Next steps

Please update your Crystal and report any issues. We will keep moving forward and start the development focusing on 0.29.

Once again, check your code with `--warnings all`. This will help in the migration of your codebase and will probably push some feedback about that tool.

Don't miss the rest of the [release changelog](https://github.com/crystal-lang/crystal/releases/tag/0.28.0) which has a lot of valuable information.

> **THANKS:**
> The development is possible thanks to the community's effort, [84codes](https://www.84codes.com/)' support, and every [supporter](https://crystal-lang.org/sponsors).
