Godot: COW mode

Created on 18 Jun 2016  Â·  17Comments  Â·  Source: godotengine/godot

In GDScript, creating an array or dictionary with literals ([] or {} respectively) will create them in shared mode.
Creating them with Array() or Dictionary() will create them in copy-on-write mode.
Not sure about pooled arrays, I think they're always in COW mode.
AFAIK this is unintended, either way it's undocumented.

GDScript probably shouldn't have access to COW stuff at all

I have a script that demonstrates the issue here: https://github.com/eska014/gdscripts/blob/b54ec14/cow_mode.gd

gdscript

Most helpful comment

Sorry to everyone who checked this issue expecting a new feature related to the milk-giving animal of the same name

All 17 comments

Sorry to everyone who checked this issue expecting a new feature related to the milk-giving animal of the same name

Array()
 ________________________________
< Your array is now in COW mode. >
 --------------------------------
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

However, this might be useful sometimes, for example:

class SomeClass:
    var array
    func some():
        array = Array()
        array.append("some stuff I want to keep in a member variable and keep 'private' but also return a copy of"
        return array

class SomeOtherClass:
    func other():
        var c = SomeClass.new()
        var someArray = c.some()
        # now I can do stuff to someArray and c.array is safe and sound.

That's just something I thought of, however it might be better to create everything shared and then when returning something you don't want modified do:
return Array(myArray)
However that doesn't work if myArray was created in shared mode. How can one even clone an array that was created using [] ?

I don't know what the correct terminology would be in GDScript, but I don't think copy-on-write is the right term here (afaik it implies a certain implementation that may or may not be used here).

In C# this would be called a value type and a reference type. Based on your code example a dictionary created with {} would be a reference type while one created with Dictionary() would be a value type.

I agree that that is very unintuitive - both that the behavior differs and that it is this way and not the other. If the two ways of creating a dictionary were different I would expect the constructor to behave like any other class constructor and create a reference type and the {}-literal to behave like integers or strings and create value types.

I agree that this "feature" might be useful in some situations but then it should be implemented using a clone()-method or a static method.

Change this world break backward compatibility, and games with it, so this have to wait a major update.

By the other hand i belive having these types copyng by reference by default and adding a duplicate() function (if not already there) is the best.

I don't know what the correct terminology would be in GDScript, but I don't think copy-on-write is the right term here (afaik it implies a certain implementation that may or may not be used here).

Godot uses a copy-on-write, opt-in shared implementation for these types, and they are contentiously exposed as such in GDScript.

yeah, I think COW should just not happen by default in general, and for all datatypes.
Was planing to take the chance since we are breaking some compatibility in 3.0 to change this

@MarianoGnu:

i belive having these types copyng by reference by default and adding a duplicate() function (if not already there) is the best.

I agree with this. There's no duplicate() function for Arrays and Dictionaries AFAIK. I think this should be added soon so games can prepare to avoid relying on the COW mode. A single thing to consider is whether this function would allow deep copies too or only shallow ones.

if you duplicate an array of references, the new array will point to the same objects, the only difference is that you can modify this array without changing the previous one, if you need to duplicate the objects on the array, no matter if it's a COW or a CHIKEN, you need to do

var new_array
for obj in array:
    new_array.push_back(obj.instance())

no matter if it's a COW or a CHIKEN

Copy Hidden Instance, Keep Existing Nodes?

My point is more for array of arrays, not to duplicate any kind of object. If you have, say, a nested dictionary with configuration you might want to make a deep copy of the default so it can be edited in isolation. Of course, you can always do with a script, but that's currently true even for a shallow duplicate.

How about doing this like Python?

var array = []
var otherArray = Array()
# both otherArray and array behave the same, so pass/copy by reference, no COW mode. Because otherArray was created without arguments
var modified = array
modified.push_back("will be in array too")
# however 
var copyArray = Array(otherArray) # copyArray is a shallow copy of otherArray
copyArray.push_back("will NOT be in otherArray")

This way, there is no need to create a duplicate() method and functionality is kept as is. Dictionary would work similarly.

Well, I can agree for both duplicate() or Array()/Dictionary() construct, it is just sugar anyway.
More important thing is deep/shallow copying. I think The copy should be deep for internal containers
(Array/Dict) with all other references being left as is. That is most intuitive approach and most useful case.

@slapin it is the most useful case until the sad moment in which you realise that it might cause huge performance drops. Also, what about recursive structures?

Okay, I'm not sure to understand your farming gibberish, but is this related ?

var A = [ "Absolutely" ];
var B = Array( A ); # <----- is supposed to make a copy of A.

B[0] = "Stupid"; 

print(A); # <---- will print "Stupid" 
print(B); # <----- will print "Stupid"

( with 2.1stable official win64)

copy constructor will not make a copy, that would break gdscript
just add a copy(deep=false) method to Dictionary and Array, and that
should be enough

On Wed, Aug 17, 2016 at 12:06 PM, SuperUserNameMan <[email protected]

wrote:

Okay, I'm not sure to understand your farming gibberish, but is this
related ?

var A = [ "Absolutely" ];
var B = Array( A ); # <----- is supposed to make a copy of A.

B[0] = "Stupid";

print(A); # <---- will print "Stupid"
print(B); # <----- will print "Stupid"

—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/godotengine/godot/issues/5277#issuecomment-240441561,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AF-Z244RdhQhmXe7BjixUeJXqWQOQt1Oks5qgyNwgaJpZM4I5A42
.

or duplicate() if you wish, since Resource and Node also uses this

On Wed, Aug 17, 2016 at 12:48 PM, Juan Linietsky [email protected] wrote:

copy constructor will not make a copy, that would break gdscript
just add a copy(deep=false) method to Dictionary and Array, and that
should be enough

On Wed, Aug 17, 2016 at 12:06 PM, SuperUserNameMan <
[email protected]> wrote:

Okay, I'm not sure to understand your farming gibberish, but is this
related ?

var A = [ "Absolutely" ];
var B = Array( A ); # <----- is supposed to make a copy of A.

B[0] = "Stupid";

print(A); # <---- will print "Stupid"
print(B); # <----- will print "Stupid"

—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
https://github.com/godotengine/godot/issues/5277#issuecomment-240441561,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AF-Z244RdhQhmXe7BjixUeJXqWQOQt1Oks5qgyNwgaJpZM4I5A42
.

Fixed in master by e6583117df95373cffb12105de82d3816ca09f85.

Was this page helpful?
0 / 5 - 0 ratings