Add two function parameter attributes @rvalue
and @universal
which must be used together with ref
(similar as auto
is only valid as auto ref
).
- An
@rvalue ref
parameter only binds rvalues, which are caller-side materialized. - A
@universal ref
parameter binds both lvalues and rvalues. - As before: Plain
ref
only binds lvalues.
For function returns, there will be no @universal ref
, but @rvalue ref
. This is, however, equivalent to ref
inside the function, meaning it must return an lvalue. The only difference between ref
and @rvalue ref
returning functions is caller-side. The result of a @rvalue ref
returning function is considered an rvalue: A move instead of a copy is issued, it cannot have its address taken, etc.
For extern(C++)
functions, @rvalue ref T
mangles like C++’s T&&
; @universal ref
is not allowed for extern(C++)
functions.
Rationale: ref
can be used to make mutations transparent to the caller. There, only lvalue arguments make sense. But ref
can also be used to alleviate copies. While there is in
(with -preview=in
), that also makes the parameter const
, and some types simply don’t work well with const
. A @universal ref
binds arguments with whatever qualifier specified, including none (i.e. mutable).
struct BigStruct;
int getN(BigStruct big) => big.n;
// ❌ copies lvalues
// ❌ result is not a reference
ref inout(int) getN(ref inout(BigStruct) big) => big.n;
// ❌ no rvalue arguments allowed
int getN(in BigStruct big) => big.n;
// ❌ can’t return by ref: big is scope
// ❌ if it could, result would be const
int getN(const(BigStruct) big) => big.n; // for rvalues
ref inout(int) getN(ref inout(BigStruct) big) => big.n; // for lvalues
// ❌ can’t simply take address anymore
// ❌ for rvalues: result is not an lvalue
ref inout(int) getN(@universal ref inout(BigStruct) big) => big.n;
// ✔️ no copies
// ✔️ returns by ref
// ✔️ allows rvalue arguments
// ✔️ single function, `auto fp = &getN` works
// ✔️ for rvalue argument: return value has lifetime to the end of the statement
// ✔️ for mutable argument: result is mutable
As far as conversions are concerned, an R function(@universal ref T)
implicitly converts to R function(ref T)
and R function(@rvalue ref T)
. The reasoning being that a @universal ref
parameter behaves exactly like a ref
parameter for lvalue arguments, so the conversion merely “forgets” that rvalue arguments would be expected and allowed. The conversion to @rvalue ref
similarly forgets that lvalue arguments were allowed.
ref
and @rvalue ref
returns are conversion-incompatible for implicit conversions. As they are not binary-incompatible, an explicit cast can be used, which is a @system
operation.