What matters is the aliasing semantics, not how something is passed. If you 
pass a ref or ptr to an object, then changes to the object's fields will be 
seen by the caller. Ditto for var parameters--a change to the parameter, and to 
its fields if it is an object, will be seen in the caller's version. For 
non-var parameters, let the compiler figure out which is best, pass by address 
or pass by copy.

Assignments are copies, either of a ref or ptr to an object, in which case 
you're creating an alias, or of an object, in which case you're copying the 
whole object. Normally such a copy is deep (copied refs/ptrs within the object 
become refs/ptrs to copies), but you can make it shallow with `shallowCopy` 
(this will change when the language adopts move semantics).

A tricky case is when a proc has a var return type (e.g., the `mitems` 
iterator, or `tables.mgetOrPut`). The return value is aliased, so modifying it 
modifies the original (e.g., the elements of `seq` iterated over, or the table 
entry), but if you assign it you've made a copy and are no longer accessing the 
original. e.g.,
    
    
    # for key not already in tab
    mgetOrPut(tab, key, newEnt).field = 3 # sets newEnt.field
    var ent = mgetOrPut(tab, key, newEnt)
    ent.field = 3 # does not set newEnt.field; only changes the copy in ent
    
    
    Run

If you want to do complex operations on a value returned by var, then pass it 
to a proc with a var parameter, which provides the needed aliasing:
    
    
    proc messWithEnt(ent: var MyEnt) =
       ent.field = 3 # changes caller's version
       ...
    
    messWithEnt(mgetOrPut(tab, key, newEnt)) # sets newEnt.field
    
    
    Run

(Note: a better API design than `mgetOrPut` would be `getOrPut[K,V](tab: 
Table[K,V], key: K, getValIfKeyNotPresent: proc (): V)` which avoids having to 
create a new object before knowing whether it is needed.)

Reply via email to