Search code examples
functionparametersreferencedstorage-class-specifier

Is an out parameter a ref whose value is implicitly reinitialized?


Dlang describes an out parameter as:

A parameter initialized upon function entry with the default value for its type.

After the parameter is initialized with a default value on function entry, isn't it essentially just a ref?

import std.stdio;

void foo(out int x)
{
    writeln(x); //prints 0
    x = 2;
}

void main()
{
    int x = 1;
    writeln(x); //prints 1
    foo(x);
    writeln(x); //prints 2
} 

I don't see any documentation comparing out to ref.
Is it accurate to conceptualize an out parameter as a shortcut from writing:

import std.stdio;

void foo(ref int x)
{
    x = x.init; //happens implicitly
    writeln(x); //prints 0
    x = 2;
}

void main()
{
    int x = 1;
    writeln(x); //prints 1
    foo(x);
    writeln(x); //prints 2
}

I don't know the intricacies of the language, so I'm worried that having this impression will cause me future grief in unforeseen circumstances.

Can a stronger distinction be made between these parameter storage classes, or is it truly a ref param that's automatically re-initialized?


Solution

  • Yes, that's all it is in implementation today, but that's not exactly what it means semantically.

    Think of out params as being additional return values rather than as arguments in the traditional sense and you should be ok. A function cannot take the address of its return value, nor can it receive data through it. An out parameter shouldn't be used in those ways either.


    D didn't used to have ref. It instead used in, out, and inout as the parameter storage classes.

    in meant (and means, it is still there) that you are going to look at, but not modify or store a reference to it (the latter is what makes it distinct from const - you are allowed to store const, but not in or scope params, which lets the compiler, in theory, optimize the memory allocation of them). It is just for data consumption.

    out means the function is going to store data in that variable, but is not going to look at or store it. The value that is already in there beforehand will be lost as the function writes its result to it. The compiler resets it upon function entry to guarantee the program doesn't depend on some value passed in through it.

    And finally, the old inout was it would take the data in, and store a value out. Today (well, starting like five years ago), this usage is long gone and inout means something entirely different (const but returning constness is conditional on the input; the const/immutable/mutable qualifiers have the same out as they do in) and the old usage was replaced with ref, which also expanded the meaning: it is no longer data going in and out, but a full-blown reference to another variable, meaning you are allowed to do things like take the address.

    While out is implemented as ref plus automatic reinitialization, you should remember the original meaning: you write data to it but don't do anything else. Do not take its address - that's legal with ref (unless it is scope ref or in ref...) but incorrect with out. You are supposed to write to it, nothing more.