I love the Crystal programming language. For the past two or three years, I have been building command-line tools with it. During this time, I often compared it with Ruby, and I encountered many differences, discoveries, and obstacles. In this article, I will share them.
1. Similarity to Ruby
Crystal looks very similar to Ruby. Many common Ruby idioms also work in Crystal. Crystal is statically typed, but most of the time you do not need to write types explicitly. Type inference will do the work for you.
2. Use DeepWiki
DeepWiki is very useful for learning Crystal. For a niche language, it is one of the best resources. You can even ask questions in your native language.
3. Arrays and Hashes cannot mix types
In Crystal, you cannot freely mix different types in an Array
or Hash
. Ruby allows this, but Crystal does not. You can use union types, but usually it is better to avoid them. Instead, consider one of these options:
At first this may feel inconvenient, but you get used to it.
# Array(Int32 | String | Symbol) - not recommended
arr = [1, "two", :three]
# OK: Union is written explicitly
arr : Array(Int32 | String | Symbol) = [1, "two", :three]
# OK: Tuple for fixed positions
t = {1, "two", :three}
# OK: record for structured data
record Item, id : Int32, name : String, tag : Symbol
items = [
Item.new(1, "apple", :fruit),
Item.new(2, "orange", :fruit),
]
4. No eval
Crystal does not have eval
. This is one big difference from Ruby.
If you really need dynamic evaluation, you should use Ruby. Another choice is to embed mruby or use a library like Anyolite. Crystal itself has an interpreter, but it is not practical and slower than Ruby or mruby.
# Ruby
code = "1 + 2"
puts eval(code) # => 3
# Crystal has no eval
# You must design differently
5. Method overloading
In Ruby, it is common to branch on the argument type inside one method.
In Crystal, it is more natural to use method overloading. This makes the code clearer.
def square(x : Int32) : Int32
x * x
end
def square(x : String) : Int32
square(x.to_i)
end
puts square(12) # => 144
puts square("12") # => 144
6. Return types should be consistent
In Ruby, a method can return values of different types. In Crystal, if the return type is not clear, you will run into trouble. If you want to return different types, you should split the method. You can use a union type, but it is not recommended.
# not recommended
def maybe_value(flag : Bool) : Int32 | String
flag ? 42 : "forty-two"
end
def value_int : Int32
42
end
def value_str : String
"forty-two"
end
7. Handling Nil
Pay attention to whether a variable can be Nil
.
If it can, you need to handle it with not_nil!
, if val = maybe_val
, or the safe navigation operator.
name : String? = nil
if n = name
puts n.upcase
else
raise "name is nil"
end
8. Garbage collection
Crystal uses LLVM and relies on an external GC (libgc
).
Performance is often close to Rust or Nim, but memory profiling and tuning can be difficult. Also, the timing of GC is not predictable, so Crystal may not be suitable for real-time systems.
9. Asynchronous I/O
Asynchronous I/O is available by default. Some developers feel it is easier to use than in Rust.
10. Linking when distributing
Crystal programs are usually linked with libgc
and other libraries such as libpcre2
. Be careful when distributing binaries.
- Linux: You can build statically linked binaries with GitHub Actions + Docker + musl
- macOS: You can prepare a Homebrew Tap, or build portable binaries with static linking for
libgc
,libpcre2
, and others
See also: github actions workflow in lolcat.cr
11. Windows support
Crystal now works on Windows (MSVC / MinGW64) more stably than before. Parallel execution also works. However, solving C library dependencies can still be painful. If you are not familiar with Windows, you may need to ask AI for help.
12. Limitations of OptionParser
The standard OptionParser
does not support combined short options.
So ls -l -h
works, but ls -lh
does not.
I plan to create a pull request to fix this in the future.
Conclusion
Writing command-line tools in Crystal is sometimes painful. But at the same time, you learn a lot. I believe the “best days” of the Crystal language are not in the past or present, but in the future.
This post was originally based on my reply to a thread on Reddit, then expanded into a Japanese article on Qiita, and now translated into English with the help of ChatGPT.
Top comments (0)