Array and Dictionaries Setget don't work as expected

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By IHate

The documentation says that when the change to the variable is made by an external source the setter function will be called and that this happens before the value is changed.

But it doesn’t seem that way with arrays and dictionaries.
The only way I get the setter to work as intended is when I duplicate the entire array/dict make the changes and then set the value to the original variable:

 #fruits = ["Pineapple","Pear,"Grapes"]
        
#This calls the setter function properly
var fruits_dup = $Child.fruits.duplicate()
fruits_dup[0] = "Orange"
$Child.fruits = fruits_dup

#This  will set the value and then call the setter
$Child.fruits[0] = "Orange"

#Array/Dict Methods will bypass the setter completely but getter is called
$Child.fruits.append("Banana")
$Child.fruits.erase("Pear")

My goal was to check if the array had changed and update my inventory only in the slots that have changed instead of having to emit a signal every time the values are changed.

There’s a changed() signal for resources but I’ve read that it’s bugged and it doesn’t work at all anyone knows something about this?

:bust_in_silhouette: Reply From: jgodfrey

The setter should only called when you set the value of the variable to a different value or object (so, the containing array or dictionary in your case). It’s not intended to be called when the contents of the containing object are changed, so the reported behavior is expected.

Also, that’s how similar functionality works in other languages as well, so there is some precedent for the behavior.

If it’s not intended to be called why is it called once I already changed the values of the array by index?

And do you know if the change signal for custom resources also works like this? Is it still bugged in 3.2 ?

Thanks

IHate | 2020-10-14 18:39

Sorry, I don’t know the answer to either of those additional questions…

jgodfrey | 2020-10-14 19:15

It may have to do with the COW (Copy-in-write) idiom. Calling operator on a COW vector will create a copy before modifying it. It is likely unintended that the setter gets called.

Seems related to this issue (will be fixed in 4.0): Assigning a value to a getter's variable results in calling its setter function · Issue #28720 · godotengine/godot · GitHub

Edit: also this issue: GDScript: Appending to an array or removing elements doesn't trigger setter · Issue #17310 · godotengine/godot · GitHub

Bernard Cloutier | 2020-10-14 19:31

:bust_in_silhouette: Reply From: max.marauder

Arrays and dictionaries in Godot are reference-types, which means that a variable of array/dictionary type stores only a reference to an array/dictionary, and this reference isn’t changed by just changing its contents, and thus setter doesn’t fire.
Duplicating it and setting the duplicate to the original variable - does fire the setter because you’re changing a reference itself.

The behavior you’d like to have is only possible in languages where arrays and dictionaries are value-types, like e.g. Swift.

Thanks for the explanation I wished the documentation mentioned this even if it’s just a short mention so I can google the rest.

But I have to ask:

why does the setter gets called when I change an index manually like in my second example:

#This  will set the value and then call the setter
$Child.fruits[0] = "Orange"

the setter will fire and print the array with the first index already changed. So what is exactly happening the setter knows something has changed but it is called too late?
What you have described explains the array methods like append,erase, etc which bypass completely the setter function. So what’s the difference when you set the index manually?

IHate | 2020-10-14 19:04

Actually the documentation does mention this:
Array — Godot Engine (stable) documentation in English

Note: Arrays are always passed by reference. To get a copy of an array which can be modified independently of the original array, use duplicate.

Regarding your second question - I have no idea, most likely it’s caused by some aspects of Array’s internal implementation, and this is a behavior I wouldn’t rely on too much.

max.marauder | 2020-10-14 21:18

I meant an explanation about the variable type I did read the note.
What I understand as reference as a complete beginner with no programming knowledge is something like a path to grab the information from. So I thought the setter would know when the values changed even if it’s not the original it has to check at some point.

I’m missing some programming basic notions I will admit.so what I meant with the mentions in the documentation would be something like links to a wiki page or something that explain the general concept otherwise i will think of reference as a non programming term with my own paradigm of what that means if that makes sense.

I also had problems with gui_input() not telling you it only admits mouseEvents and the documentation telling you it retrieves an InputEvent which is true, but it doesn’t tell you which of all of the InputEvents it retrieves I could only confirm it when I saw a GDquest video about inputs where he explicitly said so otherwise I didn’t know if it was just me been dumb.

Thanks for the answers.

IHate | 2020-10-14 22:22