Passing object by reference and mutating it

A simple MWE:

mutable struct MyHuman
    age::Int64
    MyHuman(i) = new(i)
end

# create an array
A = [MyHuman(i) for i = 1:100] 

# let's replace the 1st element of A by a new object
change_element(A[1])

function change_element(a::MyHuman)
    # check if the incoming argument refers to the same object
    println("equal before?: $(a === A[1])")  ## true
    a = MyHuman()
    a.age = 100000
    println("equal after?: $(a === A[1])")   ## false!!!
end

#A[1].age NEVER CHANGES! because I think it creates a new object in memory

Basically I have an Array of my custom type. I would like to pass in object A[1] and essentially replace this object with a new instance of the type.

For more context, consider an agent-based model where agents have an age. After a certain age, the agent dies. To maintain a constant population, I would like to replace the dead agent by a brand new agent in it’s place.

I understand that I don’t have to run the a = MyHuman() as this creates a new object in memory and reassigns my variable a. I could manually just update the fields of this person, but this will involve repeating code. In reality my MyHuman() type has many fields, and the constructor has certain logic that needs to be followed.

Any ideas?

Edit: I guess if I pass in the entire array and reassign A[1] = MyHuman() it would work, but I’d like the function to act on a single object rather than the whole array.

Yeah, that’s the problem. What you are trying to do is not possible in Julia (or most many other languages, for that matter).

This seems like exactly the right thing to do. I don’t understand what you mean about this involving repeating code–perhaps if you can describe that in more detail we can help find a solution?

Firs, your code does not run: ERROR: MethodError: no method matching MyHuman().

Second, it should be

julia> function change_element(a::MyHuman)
           # check if the incoming argument refers to the same object
           println("equal before?: $(a === A[1])")  ## true
           a.age = 100000
           println("equal after?: $(a === A[1])")   ## false!!!
       end
julia> change_element(A[1])
equal before?: true
equal after?: true

Objects are always passed by value [*]. The value might be a pointer though; since you declared MyHuman as mutable, this is the case. Therefore, a.age = 1 will mutate the object and do what you want.

[*] Ok, objects are semantically passed by value; the julia abi sometimes uses pass-by-ref as an optimization, but this is mostly invisible to users.

edit: It is very unhelpful to think about C++ for julia; instead, compare to C. The mutable keyword then tells you that you will always deal with pointers to the struct, with the same semantics. Furthermore, assignment to immutable struct fields is not supported in julia, instead you need to write the analogue of a->age = 100000;.

I definitely agree with @foobar_lv2 but if you want the behavior you describe you can use a 0-dimensional view like this:

julia> function change_element(a)
           # check if the incoming argument refers to the same object
           println("equal before?: $(a[] === A[1])")  ## true
           a[] = MyHuman(0)
           a[].age = 1000000
           println("equal after?: $(a[] === A[1])")   ## true
       end
change_element (generic function with 2 methods)

julia> change_element(view(A, 1))
equal before?: true
equal after?: true

Note that you have to use [] after the view’s name to access or change its value (you’re basically just indexing into a 0-dimensional Array).