如何在Crystal中创建二维Array?Ruby写法的等效实现
Hey there! Let's walk through fixing your 2D array issues in Crystal, starting with matching your original Ruby code and then addressing those errors you ran into.
First: Equivalent of Your Ruby 2D Array in Crystal
Your Ruby code creates a 2D array of empty arrays:
rooms = Array.new(SIZE) { Array.new(SIZE) } rooms[0][0] = :element
In Crystal, the syntax is almost identical—you just need to be explicit about types (since it's statically typed):
# Define your SIZE constant first (e.g., SIZE = 5) rooms = Array.new(SIZE) { Array(Symbol).new(SIZE) } rooms[0][0] = :element
If you don't need pre-allocated slots in the inner arrays, you could also use [] of Symbol instead of Array(Symbol).new(SIZE), but note that accessing rooms[0][0] would throw an index error unless you first add elements to the inner array.
Fixing the "instance variable not initialized" Error
Your first Map class failed because Crystal requires instance variables to be directly initialized in initialize methods—you can't use an instance variable before setting it first. Plus, Crystal distinguishes between single-quoted chars ('T') and double-quoted strings ("Test"), so you'll want to fix that too.
Here's the corrected version:
class Map @rooms : Array(Array(String)) SIZE = 5 # Replace with your actual size def initialize # First initialize the 2D array: create SIZE inner arrays, each with SIZE empty strings @rooms = Array.new(SIZE) { Array(String).new(SIZE, "") } # Now you can safely assign to the index @rooms[0][0] = "Test" end end
If you want inner arrays to start with nil instead of empty strings, change the type to Array(Array(String?)) (the ? marks it as nilable):
class Map @rooms : Array(Array(String?)) SIZE = 5 def initialize @rooms = Array.new(SIZE) { Array(String?).new(SIZE) } @rooms[0][0] = "Test" end end
Fixing the IndexError in Your Nested Loop
Your second attempt hit an index error because Array(Array(Room)).new(SIZE) creates an array of SIZE nil values—but Array(Room) is a non-nilable type, so this line is actually invalid in Crystal (you'd get a type error before even running it!). Even if you bypassed that, assigning to @map[i] might not work as expected if the array wasn't properly initialized.
Instead, initialize the 2D array with a block to ensure every inner array exists, or build rows dynamically:
Option 1: Pre-allocate with blocks (cleanest)
SIZE = 5 ROOMS = ["bedroom", "kitchen", "bathroom"] of String class Room def initialize(@type : String) end end class Map @map : Array(Array(Room)) def initialize # Initialize the 2D array with SIZE inner arrays, each pre-sized to SIZE @map = Array.new(SIZE) { Array(Room).new(SIZE) } (0...SIZE).each do |i| (0...SIZE).each do |j| room_type = ROOMS.sample puts "Create a room '#{room_type}' at cell [#{i}, #{j}]" @map[i][j] = Room.new(room_type) end end end end
Option 2: Build rows dynamically (avoids pre-allocation)
If you don't need to pre-allocate slots, build each row from scratch and add it to the main array:
class Map @map : Array(Array(Room)) def initialize @map = [] of Array(Room) (0...SIZE).each do |i| row = [] of Room (0...SIZE).each do |j| room_type = ROOMS.sample puts "Create a room '#{room_type}' at cell [#{i}, #{j}]" row << Room.new(room_type) end @map << row end end end
This approach eliminates index errors entirely because you're adding elements instead of assigning to pre-allocated (but possibly uninitialized) slots.
Key Takeaways
- Crystal is statically typed: always initialize instance variables directly in
initializemethods, and be explicit about types. - Use
Array.new(size) { Array(T).new(size) }to create properly initialized 2D arrays—this avoids nil values and index errors. - Remember: double quotes for strings (
"hello"), single quotes for single characters ('h').
内容的提问来源于stack exchange,提问作者siery

