Passing an Array to a Method: By Ref or Not

I have never had the need to specifically pass an array By Ref. I didn’t even know that it was a thing. The default behavior (no ByRef) aligned with my thinking. But you can construct an example to show that the behavior does change under some circumstances when you pass ByRef or not.

For the concrete thinker among us, here is an example

Calling Method

Var a(2) As Integer
a(0) = 1
a(1) = 2
a(2) = 3

Var b(2) As Integer
b(0) = 2
b(1) = 4
b(2) = 6

SquArr(a,b)

MessageBox("a equals: " + a(0).ToString + " " + a(1).ToString + " " + a(2).ToString+EndOfLine+"b equals: "+b(0).ToString + " " + b(1).ToString + " " + b(2).ToString)

See the Function (SquArr) that is being called by the Calling Method. Neither parameter is passed ByRef.

Private Sub SquArr(someArray() As Integer, anotherArray() As Integer)
  someArray = anotherArray
  For nIndex As Integer = 0 To someArray.LastIndex
    someArray(nIndex) = someArray(nIndex) ^ 2
  Next nIndex
End Sub

The results:

a equals: 1 2 3

b equals: 4 16 36

Now try it with the same basic Function but change the parameters:

Private Sub SquArr(ByRef someArray() As Integer, anotherArray() As Integer)
  someArray = anotherArray
  For nIndex As Integer = 0 To someArray.LastIndex
    someArray(nIndex) = someArray(nIndex) ^ 2
  Next nIndex
End Sub


The only difference is that this time you are calling: ByRef someArray.

anotherArray is not being called ByRef

The result is

a equals: 4 16 36

b equals: 4 16 36

So it clearly makes a difference. Perhaps this helps with trying to visualize this business of a pointer vs a pointer to a pointer

2 Likes

Your example is fine, but this is not accurate - “pointers to pointers” is not a useful analogy here because it is neither necessary (the behavior doesn’t require it) nor is it accurate (it isn’t implemented that way).

Well, I should have just said that the two situations are functionally different, however you want to understand it.

Happy to be corrected about the verbiage.

If i make the Object IDs visible (Settings/Debugging) i see the same ID before, inside and after exiting a method with an array paramter ByVal. With your (always good) description i would have expected another(new) ID inside the method, no?

No. The object itself is not different – Xojo never makes a copy of an object for you[1]. The reference itself would be different, if there was any way to detect that. There’s no straightforward way to demonstrate it without fooling around with MemoryBlocks, I suspect.

Think of it this way: you’ve got your object, floating around in the ether that is your computer’s memory. A reference is a variable that lets you talk to it, and you can have multiple references that correspond to the same object. The references themselves take up a small amount of memory, just like an Integer. You can have as many references to an object as you like, and while each of them gives you the same result (talk to your object), they still each have their own tiny identities and space in memory.

As a concrete example, consider this code snippet:

Var a as Integer = 45
Var b as Integer = 45

How can you differentiate between a and b in code? They have the same numeric value. But they also each have their own space in memory and can take on different values, just like reference variables can refer to the same object or different objects. Variables with the same value are indistinguishable in Xojo.

So in this example:

Var priceLookup as Dictionary = new Dictionary

Var c as Dictionary = priceLookup
Var d as Dictionary = priceLookup

…reference variables c and d both point to the same object. There’s no way in Xojo to detect any difference between c and d, because just like the Integer example above, they both hold the same value.

When you have an object that you are passing as ByVal, this is what is happening under the hood:

Method IncrementCount(ByVal someDict as Dictionary, itemName as String)
     someDict.Value(itemName)= someDict.Value(itemName) + 1
End Method

Var e as Dictionary = new Dictionary

e.Value("appleCount")=4

IncrementCount(e, "appleCount")
//Xojo makes a copy of the e variable and passes it to the ChangeName method, which essentially looks like this:
// Var newE as Dictionary
//
// newE = E   <-- it now refers to the same Dictionary as e
//
// Call IncrementCount(newE, "appleCount")
//
// Upon return, newE is discarded and reference variable e is left unchanged because the IncrementCount method had no access to it. The Dictionary that newE and e referred to is updated with the new apple count.

To bring the discussion full circle back to your original question: object IDs are attributes of objects, not references. You could have a hundred references to a single object and they would all return the same object ID. In my example above, e and newE (while it exists) both refer to the same object and thus display the same object ID.

1 - The exception to this rule is String, which is the only object I can think of that Xojo automatically copies.


  1. Footnotes ↩︎

2 Likes

Thanks a lot, Eric!

That is exactly how it is implemented. Xojo prefers the term “reference” over “pointer”, but under the hood, they are pointers (and pointers to pointers) just like any other language.

2 Likes

Prove that they are pointers to pointers, then. I see no reason for such a structure in this discussion.

I have been told you will discover it for yourself should you write a plugin.

Even if they are, that’s an implementation detail that is never surfaced insofar as Xojo code behaves, and has no impact on how any Xojo code should be written. I don’t even see how you could prove it without access to the internals (either the Xojo source itself, or whatever is exposed to a plugin). It simply isn’t a consideration at the Xojo code level.

While that may be true, your misguided assertion that they aren’t pointers is not helpful and comes across as a putdown of Micheal and Robert. There are many of us who find it easier to think of it in terms of pointers. Please don’t belittle those who do.

2 Likes

Xojo is compiled to machine language: memory addresses, immediate values, registers, stack, etc. It doesn’t matter what language you’re using, what it calls things, or how it abstracts what is happening in hardware.

An array is a memory address of the start of the array. The index is an offset from that base address. This is abstracted in Xojo in that we don’t have an operator to get the address and aren’t allowed general use of Ptr. The closest that comes to mind is the ability to assign nil to an array. It works because the array variable is a memory address, which can be nil.

An array passed by value is the memory address of the array. An array passed by reference is the memory address of a memory address of the array. These memory addresses are called pointers in computer science. I don’t see what is meaningful about insisting they be called references if a reference is just what you’re calling a memory address.

The Xojo documentation mentions that using an array for assignment or as a parameter creates a “new variable that points to the original array”. The Xojo documentation for ByRef says “When you pass information by reference, you actually pass a pointer to the variable containing the information.” If there is some additional layer of implied abstraction, I’m not seeing it.

I certainly don’t mean to put anybody down, and I apologize if it came off that way. My point is that although they may be implemented behind the scenes as pointers, there’s no advantage of thinking of thinking of them that way when programming in Xojo. You can’t do pointer arithmetic, etc; so while some devs may mentally model them as pointers, Xojo references don’t gain much from being contextualized like that.

1 Like

It is absolutely amazing to me that through all of this discussion and back and forth that you guys are still being vague about the context from whence you are talking.

Since this is a Xojo forum and someone might stumble on this years from now and get the wrong impression without any clarification from reality, lets talk about Xojo exclusively.

Lets address the issues surrounding Arrays, Objects, ByRef an ByVal first. I just tested all this in a Console App to be absolutely sure that nothing has changed since I left in 2022, so barring any platform specific deviations (I’m looking at you Android), these are the rules that have been in existence from the distant past.

  1. Xojo Arrays are always Objects.
  2. Objects passed to methods are always passed by reference.
  3. Using ByVal or ByRef on an Object does not get you a pointer to a pointer. The modifier is ignored.

Regarding the note in the documentation:

Passing array parameters with ByRef is not currently supported for Android.

I find that unfortunately misleading given the rules/observations above because it doesn’t actually tell us what the problem is…

  • Is the problem the ByRef keyword itself and simply omitting it fixes the problem.
  • Is the problem trying in to send the array as a reference and thus being able to modify its contents within the method, affecting the original array like all the other platforms do.

I unfortunately don’t have a working Xojo <=> Android configuration to test it, but my hope is that it’s the former and not the latter because the latter makes the cross-platform nature of Xojo one step further from reality.

A simple test suggests otherwise. ByRef replaces the original array variable.

Button1.Pressed

var a() as integer = array(1,2,3)

modify(a)
break
Public Sub modify(byref b() as integer)
  var c() as integer = array(10,11,12)
  
  b(1) = 5
  b = c
End Sub

Wilthout ByRef, a is (1,5,3)
With ByRef, a is (10,11,12)

Maybe we’re talking about different issues.

3 Likes

That is not the behavior I’m seeing in 2024r4.2

The behavior @Tim_Hare describes is what I see in 2024r4.2 and 2025r2.1.

1 Like

Here’s a demonstration: https://travi.sh/Array-ByRef.xojo_binary_project-CZo9lLbpYZ.zip

My output is as I expect:

Create array a
a(1, 2, 3)

Create array b and assign a to it
(b is a reference to a)
a(1, 2, 3)
b(1, 2, 3)

Modifying the contents of b
(which modifies a because b is a reference to a)
a(1, 2, 4)
b(1, 2, 4)

Modifying the value of b using ByRef
(which does not modify a because we've only changed the b's reference)
a(1, 2, 4)
b(123, 456, 789)
5 Likes

And this is working as documented:

Arrays and objects are reference types. So when you don’t use ByRef, what gets passed for a value type is a copy of the value. For a reference type, a copy of the reference is passed. Since it’s still a reference, you can access and modify the original array or object through it.

and

Now, let’s add ByRef into the picture:

With a reference type…you get a reference to the reference, allowing you to change what the original reference points to.

and

a reference is a pointer

So it’s a reference to a reference, a reference is a pointer, the ByRef keyword has an effect, and Xojo works in a way consistent with other languages.

If you see different behavior please create an issue.

2 Likes

I’m always sure I’m doing this wrong. Your example has finally shown me what actually happens in an easy to see way. Thank you!

1 Like