Crystal Data Types — Numbers, Strings, Arrays, Hashes, and Named Tuples
In this tutorial, you will learn about Crystal Data Types. We cover key concepts, practical examples, and best practices to help you master this topic.
Crystal combines Ruby-like syntax with static typing via type inference. Its type system catches errors at compile time while feeling as flexible as Ruby at write time.
In this tutorial, you'll explore Crystal's data types. Crystal is ideal for performance-sensitive applications that need Ruby's expressiveness with C-like speed — a common use case at DodaTech for security tooling.
What You'll Learn
- Primitive types (Int32, Float64, etc.)
- Strings and interpolation
- Arrays and their generic type
- Hashes for key-value storage
- Named tuples and tuples
- Nilable types and unions
- Type inference basics
Primitive Types
# Integers (multiple sizes)
age = 30 # Int32
big = 1_000_000_000 # Int32
small : Int8 = 127 # Explicit type
large = 999999999999 # Int64 (auto-promoted)
# Floats
pi = 3.14159 # Float64
f32 : Float32 = 1.5 # Explicit Float32
# Type conversion
42.to_f64 # => 42.0
3.14.to_i32 # => 3 (truncates)
"123".to_i # => 123
# Operations
puts 10 / 3 # => 3 (integer division)
puts 10.0 / 3 # => 3.33333
puts 10 % 3 # => 1
Strings
name = "Crystal"
puts "Hello, #{name}!" # Interpolation
puts "Length: #{name.size}" # => 7
# String methods
"hello".upcase # => "HELLO"
"hello".capitalize # => "Hello"
"hello".reverse # => "olleh"
"a,b,c".split(",") # => ["a", "b", "c"]
# Heredoc
multi = <<-TEXT
Line 1
Line 2
TEXT
# Char
letter : Char = 'A'
Arrays
# Generic array (type inferred)
numbers = [1, 2, 3] # Array(Int32)
names = ["Alice", "Bob"] # Array(String)
# Mixed types become union
mixed = [1, "two", 3.0] # Array(Int32 | String | Float64)
# Explicit type
scores : Array(Int32) = [95, 88, 92]
# Operations
numbers << 4 # Append => [1, 2, 3, 4]
numbers + [5, 6] # Concat
numbers.first # => 1
numbers.last # => 4
numbers[1..2] # => [2, 3]
# Map/filter/reduce
[1, 2, 3].map { |n| n * 2 } # => [2, 4, 6]
[1, 2, 3].select { |n| n > 1 } # => [2, 3]
[1, 2, 3].sum # => 6
Hashes
# Key-value with type inference
user = {"name" => "Alice", "age" => 30}
# => Hash(String, Int32 | String)
# Symbol-like keys (using string or symbol)
config = {port: 8080, host: "localhost"}
# => Hash(Symbol, Int32 | String)
# Access
puts config[:port] # => 8080
puts config[:port]? # => 8080 (returns nil if missing)
puts config.get(:missing) { 3000 } # => 3000 (default)
Named Tuples
# Named tuple — typed, immutable, fast
point = {x: 10, y: 20} # NamedTuple(x: Int32, y: Int32)
puts point[:x] # => 10
# Regular tuple (positional)
pair = {1, "two"} # Tuple(Int32, String)
puts pair[0] # => 1
Nilable Types
# Any type can be nilable (T | Nil)
name : String? = nil # String or Nil
name = "Alice" # Also valid
# Safe navigation operator
user = {"name" => "Alice"}
puts user["name"]?.nil? # => false
puts user["missing"]? # => nil
# Nil-check with if
if value = user["key"]?
puts "Found: #{value}"
else
puts "Not found"
end
Practice Questions
Create a hash representing a book and access its title, author, and year.
Write a function that takes an array of integers and returns only the even numbers.
Use a named tuple to represent a 3D point {x, y, z} and calculate distance from origin.
Write a nil-safe access chain using
?.syntax.Convert a string "1,2,3,4,5" to an array of integers using split and map.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro