For a start, a few points before the explaination,
- The two are the same
- Assignment to a (untyped) local variable is NEVER significant in julia. (The previous point comes out of this)
In this particular case, due to the ABI of the type Dict
and because Dict
is an abstract type, yes, a pointer is passed, and it is a pointer in both cases.
For a deeper explaination, you should note that julia and c++ has completely different object model. Similar to most scripting language, julia does not associate/equate an object with the variable (name) that refers it.
When you have auto a = T{};
in c++, a
is the object and whenever you assign to a
you are mutating that object. However, in julia, for a = T()
, a
is not the object, it merely refer to the object. Semantically (although not correct as a performance model) you can think of all julia variables/fields/array slots as a pointer to the actual object. You are always passing references (think pointers) of objects. This should explain why the two cases you have above makes no difference. Julia simply doesn’t have the copying problem C++ have. This doesn’t mean that julia never need to copy data around (more on that below) but that’s purely a compiler problem and after you specified how the data should be stored, the compiler has all the freedom to decide whether the copy needs to be done/whether the construction can be done in place.
(It is wrong semantics wise but implimentation wise you can sort of thinking all julia object always having a trivial copy constructor so the compiler can always elide that)
Now you will think this is inefficient and you’ll be right if the pointer is actually used all the time. However, a few things that helps.
- For local variable, the compiler can do whatever it want. This means that for a local variable of type
Int
, the compiler won’t actually generate that pointer and will do all the same tricks a c++ compiler can do.
- For fields and array slot, julia have immutable types. This gives the ABI freedom to not use the pointer (the reason for this is explained in the doc and I don’t really want to repeat although I could if you ask…) so the ABI will actually store a “copy” of the immutable object in a concrete type field/array slot.
(2) gives you control on the layout of your object (with limitations and a mutable object will never be stored inline) and if you put a big immutable struct inline to another struct, the compiler might be asked to copy data into it. However, as mentioned above, since the copy is always trivial, the compiler can usually elide that when it’s unnecessary (or when a c++ compiler can in a similar case).
Tthe capture rule is not affect by where the function is defined. It capture anything it needs from the local scope. Note that struct members is not a scope so accessing field
as a variable won’t work. If you assign the result of new
to a variable then accessing that variable from the function will give you access to the enclosing object.
You also never need to pass the pointer to the object, you can just capture the object directly. You can also do that for an immutable type since the captrue variable doesn’t have to be defined when you define the function, i.e. a = new(()->a)
should work. If you want to emulate method call though, overloading getproperty
will be better.