Skip to content
GitHub Repository Forum RSS-Newsfeed

Crystal 0.30.0 released!

Brian J. Cardiff

Crystal 0.30.0 has been released!

This release comes with some language changes, compiler features, and std-lib improvements. There is some work-in-progress related to multi-threading in other branches, they are moving forward and we are eager to share them as a whole.

There are 139 commits since 0.29.0 by 28 contributors.

Let’s review some highlights in this release. But don’t miss the rest of the release changelog which has a lot of valuable information.

Language changes

The language now checks that the return type of an abstract method is obeyed. This means that in an implementing type you need to annotate explicitly the return type if the abstract method is also annotated.

The following is now an error

abstract class Foo
  abstract def foo : String

class Bar < Foo
  def foo

You will need to declare Bar#foo as def foo : String which makes the underlying issue more evident: Bar#foo was wrongly trying to return an Int32 instead of a String.

You don’t need to copy and paste it. A subtype can be used.

class Parent; end
class Child < Parent; end

abstract class Foo
  abstract def foo : Parent

class Bar < Foo
  def foo : Child

Finally, if you forget to implement some abstract method the compiler will let you know. Even on generic types and modules.

class Foo
  include Enumerable(Int32)

In src/

 37 | abstract def each(&block : T -> _)
Error: abstract `def Enumerable(T)#each(&block)` must be implemented by Foo

Since this change is likely to break a good amount of code it is introduced as a warning. Build your project with --warnings=all to be ready for when this is switched from a warning to a real compile-time error. Read more about this feature in #7956, #7999, and #8010.

Syntax changes

A small breaking-change syntax wise is that ranges can not span across lines since #7888.


There are a couple of new macro methods to extract arguments of Annotations in #7694. For all metaprogramming lovers, this would be good stuff. As usual, metaprogramming is not something to abuse.

The API to work with AST nodes of paths and types had some polishing that will bring some fewer headaches. Read more in #7970.


Support for LLVM 7 was a long debt. We finally paid it together with support for LLVM 8. Read more about it in #7987 and #7986. In case you want to contribute in two months with the LLVM 9 support ;-)

There are a couple of bug-fixes related to top-level private const scope, annotation lookup on generic types and other corner cases.


In case you missed the news, the nightly Linux release is available in and in TravisCI as crystal: nightly in your travis.yml. This was automated in #7893.

For macOS official packages (ie: the one that you can download from GitHub) we are now using LLVM 6.0.1. Don’t ask what has been used up until today.

For Homebrew, LLVM 8.0.0 is used from now on.

Standard library

There was a need for a breaking-change in UUID: the method UUID#to_slice was removed because it exposed a pointer to the underlying memory representation and doing that in a stack allocated value is error-prone. Use UUID#bytes instead, that returns a StaticArray(UInt8, 16). Read more at #7901.

Boxing of values using Box is used for some C callbacks and some low-level structures. Thanks to #8016 the boxing of nil and reference types is now lighter.

Benchmark.memory is now available to measure just memory usage. Read more at #7835.

Every now and then some sample code in the docs are outdated. There is a tool made and run regularly to keep them in good shape. Luckily #8003 was not as big as other past iteration. Check maiha/crystal-examples to see how this is done!


JSON serialization can handle numeric keys, so Hash(Int32, String).from_json works. Read more at #7944.

YAML deserialization handles String as the last option if the expected type is a union. Since anything can be a String that make sense. Read more at #7938

Finally there is a breaking-change regarding the API on the JSON parser. The usage of symbols for the parser state was replaced by enums in #7966.


URI.escape and URI.unescape are deprecated and you will need to choose between URI.encode_www_form/URI.decode_www_form and URI.encode/URI.decode. This allows you to choose how to handle reserved chars. Read more at #7997.

The parsing of the HTTP protocol got some performance optimizations in #8002 and #8009. This might improve the performance of HTTP::Server in your apps.


There is an initial API to work with users and groups in System::User and System::Group. Read more at #7725.

Other news

Snapcraft 3.7 has been released and that means that the Crystal plugin can be used out of the box.

Next steps

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

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. Some future language changes were introduced in this release as warnings for a smooth migration. But, unless you use --warnings=all you will not discover them.

It will also be helpful if your shards are run against Crystal nightly releases. Either Docker or Snap are the current channels to get them easily. This will help to reduce the friction of a release while checking if the ecosystem is in good shape.

Stay tuned!


The development is possible thanks to the community’s effort, 84codes’ support, and every supporter.