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: 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)