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)
- 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
Assessment
This issue has not been assessed yet.