MATCH works differently than IF

Godot Version

4.2.2.stable.official

Question

I’d like to understand why in a specific case, “match” and “if” work differently.
I have a code like this:

enum TileState { LOCKED, SOIL_BAD, SOIL_GOOD, GRASS, WATER }
...
func spawn_world_tiles() -> void:
	for tile_key in world_data.tile_states_save_data: 
		var tile_value = world_data.tile_states_save_data[tile_key]
		match tile_value:
			TileState.SOIL_GOOD:
				print("yes match")
		
		if tile_value == TileState.SOIL_GOOD:
			print("yes if")		

I would expect, that “yes match” and “yes if” should always be printed at the same time, but no.

I create the dictionary of tiles like this

var tile_state_data = { 
		Vector2(-1, -1): World.TileState.GRASS, 
		Vector2(-1, -2): World.TileState.SOIL_GOOD, 
		Vector2(0, 0): World.TileState.GRASS, 
                ...

And I should also mention that if I just create it in memory, both MATCH and IF work. But If I serialize the tile_save_data to JSON and then load it from a file, only IF works but MATCH does not.

This is the code that loads the dictionary from file/JSON:

	for tile_state_key in json.data["world_save_data"]["tile_states_save_data"]:
		var parsed_tile_state_key = str_to_var("Vector2" + tile_state_key) as Vector2i
		var tile_state_value = json.data["world_save_data"]["tile_states_save_data"][tile_state_key]
		world_save_data.tile_states_save_data[parsed_tile_state_key] = tile_state_value

Maybe it is just my mistake, but I spent many hours debugging and looking for explanations, haven’t found any. It seems like a data type related problem, but still I would really like to understand why IF works in this case and MATCH does not.

The difference in behavior between the match statement and the if statement in your Godot code is likely due to the type of tile_value after loading it from JSON. When loading from JSON, data types might not be preserved exactly as you expect, leading to mismatches in the match statement which relies on strict type comparison.

Explanation:
When you serialize and deserialize the tile_states_save_data, the values might be interpreted as integers (or strings if not handled correctly) instead of the TileState enum type.

The match statement in Godot does a strict comparison, which means it checks both the value and the type. If tile_value is an integer when it should be of type TileState, the match statement will fail.

The if statement in the other hand performs a value comparison, which can succeed even if tile_value is an integer and TileState.SOIL_GOOD is an enum value because they might both have the same underlying integer value.

Solution:
Ensure that the values loaded from JSON are converted to the correct TileState enum type. You can achieve this by explicitly casting or converting the values during or after loading.

Here’s how you can modify the loading process:

Edit: Changed to working type casting

for tile_state_key in json.data["world_save_data"]["tile_states_save_data"]:
		var parsed_tile_state_key = str_to_var("Vector2" + tile_state_key) as Vector2i
		# Enforce the enum type via typed variable
		var tile_state_value:World.TileState = json.data["world_save_data"]["tile_states_save_data"][tile_state_key]
		world_save_data.tile_states_save_data[parsed_tile_state_key] = tile_state_value

By converting tile_state_value to the TileState enum, you ensure that tile_value has the correct type, and the match statement will work as expected.

1 Like

Thank you Timothy!

The key piece here then is, that MATCH and IF works differently regarding data types and that is what I had no clue about. This alone resolves my request.

Also, I tried the code you provided, but I seem not to be able to convert the int value to the enum, not even in the way you described (Godot says that the enum is not a function).

However I tried enforcing the type in the following way, which works for me:

	for tile_state_key in json.data["world_save_data"]["tile_states_save_data"]:
		var parsed_tile_state_key = str_to_var("Vector2" + tile_state_key) as Vector2i
		# Enforce the enum type via typed variable
		var tile_state_value:World.TileState = json.data["world_save_data"]["tile_states_save_data"][tile_state_key]
		world_save_data.tile_states_save_data[parsed_tile_state_key] = tile_state_value
1 Like

That’s great! Thank you for the correction.

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.