What is the true meaning of pass-by-reference in modern languages like Dart?
Quick answer: what gets passed to your functions cookEggs
and cookOne
are references to the objects, not to the variables (which would be real pass-by-reference).
The term pass-by-reference is often misused to mean pass-references-by-value: many languages only have pass-by-value semantics, where the values that are passed around are references (i.e. pointers, without the dangerous features). See Is Java "pass-by-reference" or "pass-by-value"?
In the case of cookEggs(eggList)
…
…the variable eggList
contains a reference to a list of eggs. That reference was initially given to you by the expression new List()
, and you're passing it to cookEggs
after storing meanwhile in your variable eggList
. Inside cookEggs
, adding to the list works, because you passed a reference to an actual, existing list object.
In the case of cookOne(single)
…
…the variable single
has only been declared, so it was implicitly initialized by the language runtime to the special reference null
. Inside cookOne
, you're replacing which reference is contained in egg
; since single
is a different variable, it still contains null
, therefore the code fails when you try to use that.
To clarify
The pass-references-by-value behavior is common to a lot of modern languages (Smalltalk, Java, Ruby, Python…). When you pass an object, you're actually passing-by-value (therefore copying) the contents of your variable, which is a pointer to the object. You never control where objects really exist.
Those pointers are named references rather than pointers, because they are restricted to abstract away the memory layout: you can't know the address of an object, you can't peek at the bytes around an object, you can't even be sure that an object is stored at a fixed place in memory, or that it's stored in memory at all (one could implement object references as UUIDs or keys in a persistent database, as in Gemstone).
In contrast, with pass-by-reference, you'd conceptually pass the variable itself, not its contents. To implement pass-by-reference in a pass-by-value language, you would need to reify variables as ValueHolder objects that can be passed around and whose contents can be changed.
That's because, like Java, Dart is always pass-by-value, and never pass-by-reference. The semantics of passing and assigning in Dart is the same as in Java. Look anywhere on StackOverflow about Java and pass by value to see why Java is described as always pass-by-value. Terms like pass-by-value and pass-by-reference should be used consistently across languages. So Dart should be described as always pass-by-value.
In actual "pass-by-reference", e.g. when a parameter is marked with &
in C++ or PHP, or when a parameter is marked with ref
or out
in C#, simple assignment (i.e. with =
) to that parameter variable inside the function will have the same effect as simple assignment (i.e. with =
) to the original passed variable outside the function. This is not possible in Dart.