Crystal 1.21.0 is released!
Highlights
- Execution contexts enabled by default stdlib/runtime
-
%Wstring array literals with interpolation lang/syntax - Split manpages infrastructure
We are announcing a new Crystal release 1.21.0 with several new features and bug fixes.
Pre-built packages are available on GitHub Releases and our official distribution channels. See crystal-lang.org/install for installation instructions.
Stats
Section titled StatsThis release includes 161 changes since 1.20.3 by 21 contributors. We thank all the contributors for all the effort put into improving the language! ❤️
Changes
Section titled ChangesBelow we list the most remarkable changes in the language, compiler, and stdlib. For more details, visit the full changelog.
Breaking
Section titled BreakingWe expect little disruption from the introduction of execution contexts. But it can break code with specific expectations about concurrent behaviour, especially about the fact that a fiber stays on the same thread. Read more about this in the next section.
Another breaking change is that we disabled the automatic fallback to legacy
PCRE, which has been deprecated for many years now. You can still explicitly
opt-in with compiler flag -Duse_pcre or environment variable USE_PCRE1=1
(#16924).
Thanks, @straight-shoota
If you notice any unexpected issues, please let us know in the issue tracker or forum.
Execution contexts
Section titled Execution contextsExecution contexts from RFC 0002 are enabled by default. We have written an entire blog post about it: Releasing Execution Contexts.
The default context has parallelism 1, i.e. it is single-threaded. It can be explicitly resized to run multiple fibers in parallel, or you can create additional contexts for parallelism.
As a result, fibers are no longer pinned to one thread. Even with parallelism 1, fibers may switch threads. The only guarantee is that no more than one fiber runs at any time. Code that relies on thread-local state across fiber yields should be reviewed.
The concrete breaking changes are:
- Fibers in parallel execution contexts can resume in a different thread.
- Fibers in concurrent execution contexts can switch threads on a blocking syscall.
- Execution contexts don’t support
spawn(same_thread:). In a parallel execution context,same_thread: trueraises at runtime.
See Releasing Execution Contexts: Breaking Changes for details.
This effort is part of the ongoing project to improve multi-threading support with the help of 84codes.
Thanks @ysbaddaden
Percent string array literal with interpolation
Section titled Percent string array literal with interpolationRFC 0021 introduces %W, a string array literal that complements %w with
escape sequences and interpolation, including splats (#16919).
%W[foo\ bar baz] # => ["foo bar", "baz"]
%W[foo #{"bar"} baz] # => ["foo", "bar", "baz"]
%W[foo #{1 + 1} baz] # => ["foo", "2", "baz"]
%W[foo #{"bar"}baz#{"bab"} qux] # => ["foo", "barbazbab", "qux"]
%W[foo _#{"bar"}_ baz] # => ["foo", "_bar_", "baz"]
%W[foo #{"bar baz"} qux] # => ["foo", "bar baz", "qux"]
%W[foo #{*%w[bar baz]} qux] # => ["foo", "bar", "baz", "qux"]
Thanks, @straight-shoota
Macros
Section titled Macros-
TypeNode#all_methodscomplements#methodsby including methods from a type’s ancestors in addition to methods defined directly on the type (#16902).Thanks, @Blacksmoke16
Standard library
Section titled Standard library-
Channel(T)implementsIterator(T). This allows channels to plug directly into iterator-based APIs and chaining patterns (#16916).channel = Channel(Int32).new spawn do 3.times { |i| channel.send(i) } channel.close end channel.to_a # => [0, 1, 2]Thanks, @jgaskins
-
HTTP::WebSocket#receiveis a synchronous call that enables a request/response style loop with blocking reads (#16689). This can be easier to use than the callback-based#runloop.Thanks, @jgaskins
-
The
-Dwithout_mainflag allows building a binary without a main function, for example to serve as a dynamic library (#17074).Thanks, @kojix2
-
JSON::Fieldnow supports nested root properties (#17016), making it easier to map deeply nested payloads to flat object properties.Thanks, @tcoatswo
-
URI::Params::Fieldaddsignore:,ignore_serialize, andignore_deserializeoptions (#17029).Thanks, @trypsynth
-
Spec matchers
#shouldand#should_notcan be chained to write more expressive expectations (#17020).value.should be_a(String).should_not be_emptyThanks, @straight-shoota
-
Process::Status.[]is a portable and explicit constructor for process exit statuses (#17003).Thanks, @straight-shoota
-
String#present?is a convenient predicate for intent-revealing checks in guards and validations (#16930).Thanks, @Sija
-
UUID.v6andUUID.v8generators (#17010).Thanks, @trypsynth
-
Using
mmapto read ELF, Mach-O, and PE files as well as some DWARF sections reduces overhead for parsing debug info (#16939).Thanks, @ysbaddaden
Experimental: Socket#sendfile
Section titled Experimental: Socket#sendfile
Socket#sendfile (and Crystal::EventLoop::Socket#sendfile) enables
efficient file-to-socket transfer paths with fewer user-space copies (#16665).
File.open("foo/bar") do |file|
socket.sendfile(file)
end
Thanks, @ysbaddaden
Compiler
Section titled Compiler-
Branches in
caseexpressions with multiple type expressions now split into separate restrictions for each type (#17072).case x; when Bar, Bazis now equivalent toif x.is_a?(Bar) elsif x.is_a?(Baz). Previously, it was equivalent toif x.is_a?(Bar | Baz). The latter can still be expressed aswhen Bar | Baz.Thanks, @straight-shoota
-
Improved debug metadata for procs (#16830), constants and class vars (#16831), and LLDB collection formatters (#16832) for better inspection and clearer debugger output.
Thanks, @skuznetsov
-
ASTNode#inspectnow prints an unambiguous representation of the syntax node and its properties, instead of folding into the source representation (#16699).Thanks, @straight-shoota
Dependencies
Section titled Dependencies- OpenSSL 4.0 (#16839)
Thanks, @straight-shoota
Deprecations
Section titled DeprecationsColorize.on_tty_only!is obsolete because its behaviour is the default since #15881 (#16859).spawn(same_thread:)only makes sense for legacy-Dpreview_mtand is pointless for execution contexts (#17097). See Execution ContextsIO::Memory.new(writeable:): the parameter has been renamed towritable(#16858).- The platform-specific
Process::Status.newconstructor is hidden to avoid accidental misuse (#16997). A portable constructorProcess::Status.[]is available as replacement.
Thanks, @straight-shoota, @ysbaddaden
Manpages
Section titled ManpagesSince 1.16, we’ve split the historic single manpage crystal(1) into individual
pages for each subcommand. Now we’ve removed the original manpage from the
repository. Manpage consumers must use the split manpages now.
We only provide the adoc sources, and package build scripts need to generate the
roff format themselves (requires asciidoctor).
Package maintainers who ship manpages should update to the new build instructions if they haven’t already.
- Makefile: Build manpages for
installtarget (#16991)
Thanks, @straight-shoota
We have been able to do all of this thanks to the continued support of 84codes and every other sponsor. To maintain and increase the development pace, donations, and sponsorships are essential. OpenCollective is available for that.
Reach out to crystal@manas.tech if you’d like to become a direct sponsor or find other ways to support Crystal. We thank you in advance!
Contribute