godotengine / godotengine/godot

Modifying for loop variable while iterating through dictionary results in instability with the dictionary (in other words dictionary[dictionary.keys()[0]] resulting in an error when dictionary.keys()[0] exists)

Open
#116,705 2 comments 0 reactions 0 assignees View on GitHub
discussion documentation needs testing topic:gdscript
Dominant language
C++
Stars
117k
Forks
26.8k
PR merge metrics
PR metrics pending

Description

### Tested versions

Reproduceable in: v4.5.1.stable, v4.6.1.stable

### System information

Godot v4.6.1.stable - Windows 11 (build 26200) - Multi-window, 2 monitors - Direct3D 12 (Mobile) - dedicated NVIDIA GeForce RTX 3080 Ti (NVIDIA; 32.0.15.9174) - 12th Gen Intel(R) Core(TM) i5-12600K (16 threads) - 63.73 GiB memory

### Issue description

I have a workaround. I'm using Arrays as keys in a dictionary. While iterating over the Dictionary, I first check to see if I've done the necessary logic already for each Dictionary entry. If I haven't, then I need to do logic on each entry in the Dictionary that's part of the sequence starting at the lowest value in the second entry of the key. To test that out, I create a tempKey which I then use for finding the lowest value in the second entry of the key. Now on the godot docs page, it says "Note: Erasing elements while iterating over dictionaries is not supported and will result in unpredictable behavior." and I'm not doing that. What I'm being hit by is that on Arrays it talks about "Note: Arrays are always passed by reference" and as it turns out that when the for loop iterates over a dictionary that has arrays for keys, that it's also a reference to the actual key and not a duplicated copy. The workaround is to create a duplicate copy. However, not creating a duplicate copy, and just modifying the key, results in instability, which is why I'm reporting this as a bug. The instability in question is that although the first key entry exists in the dictionary, it is inaccessible, and will result in an error/ crash. Depending on where and how the key is modified, it might not result in the instability, but I found a way that was reproduceable.

In other words, regardless of whether the keys in the dictionary should be modified after assigning values in the dictionary because they're passed by reference. dictionary[dictionary.keys()[0]] should never result in an error as long as dictionary.keys()[0] exists. And it can when modifying the keys while iterating through the dictionary.

### Steps to reproduce

var funnyDictionary: Dictionary[Array, int]= {
[0,2]: 1,
[0,3]: 1,
[0,1]: 1,
[0,7]: 0,
[0,8]: 0,
[0,9]: 0
}

for n in funnyDictionary:
if(funnyDictionary[n]==0):
continue
#Sequential logic needed in this grouping. Need to find smallest key. In this sample the following commented line would fix the error. "var p = n.duplicate()"
var p = n
while(funnyDictionary.has(p)):
p[1]= p[1]-1
p[1]= p[1]+1

while(funnyDictionary.has(p)):
funnyDictionary[p]=0 #doing logic. In my actual project, I needed to duplicate p right below this line as well in order to fix the error. "p=p.duplicate()"
p[1]= p[1]+1

print(funnyDictionary) #can print the results, albeit it looks a little odd. "{ [0, 4]: 0, [0, 3]: 0, [0, 1]: 0, [0, 7]: 0, [0, 8]: 0, [0, 9]: 0 }"
print(funnyDictionary.keys()) #can access most entries inside of funnydictioanry. "[[0, 4], [0, 3], [0, 1], [0, 7], [0, 8], [0, 9]]"

print(funnyDictionary[funnyDictionary.keys()[1]]) #Can access second entry inside of funnydictionary. "0"
print(funnyDictionary[funnyDictionary.keys()[0]]) #Cannot access first entry inside of funnydictionary. This results in error "Invalid access to property or key '[0, 4]' on a base object of type 'Dictionary[Array, int]'."

### Minimal reproduction project (MRP)

N/A

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.