guidelines for parameter types
Dan
dbdavidson at yahoo.com
Mon Dec 17 12:46:25 PST 2012
Assume V is a non-template parameter type and v is a parameter of
that type for any function. Also assume T is a template parameter
type and t is a parameter of that type for any function. Is the
following table and set of guidelines below reasonable? What
other guidelines do you use or would make sense to follow? I
apologize if this is obvious/well known and I consider myself new
to D, so please address any of my misconceptions. I'm finding
that on the surface D sounds much simpler than it is, but if I
can get a good set of guidelines it should all work out.
Thanks,
Dan
(COW means Copy on Write)
| convention | what it means/when to use
|
|-------------------------+-------------------------------------------|
| V v | V is primitive, dynamic arr, assoc
array, |
| | COW, or kown copy cheap (< 16 bytes)
|
| |
|
| const(V) v | same as (V v), pedantic - makes copy
and |
| | guarantees no mutation in function
|
| |
|
| immutable(V) v | No need - for ensuring no local
changes |
| | prefer 'const(V) v'
|
| |
|
| ref V v | Use only when mutation of v is
required |
| |
|
| ref const(V) v | Indicate v will not be changed,
accepts |
| | {V, const(V), immutable(V)}
|
| |
|
| ref immutable(V) v | No need - restrictive with no benefit
|
| | over 'ref const(V) v'
|
| |
|
| V* v | Use only when mutation of v is
required. |
| | Prefer 'ref V v' unless null
significant |
| | or unsafe manipulations desired
|
| |
|
| const(V)* v | Indicate v will not be changed,
|
| | accepts {V*, const(V)*,
immutable(V)*} |
| | still prefer ref unless null
significant |
| | or unsafe manipulations desired
|
| |
|
| immutable(V)* v | No need - restrictive with no benefit
|
| | over 'const(V)* v'
|
| |
|
| T t | T is primitive, dynamic array, or
assoc |
| | array (i.e. cheap/shallow copies).
For |
| | generic code no knowledge of COW or
|
| | cheapness so prefer 'ref T t'
|
| |
|
| const(T) t | same as (T t), pedantic - makes copy
and |
| | guarantees no mutation in function
|
| |
|
| immutable(T) t | No need - for ensuring no local
changes |
| | prefer 'const(V) v'
|
| |
|
| ref T t | Use only when mutation of t is
required |
| | prefer 'ref const(T) t' if mutation
not |
| | required
|
| |
|
| ref const(T) t | Indicate t will not be changed,
accepts |
| | {T, const(T), immutable(T)} without
copy |
| |
|
| ref immutable(T) t | No need - restrictive with no benefit
|
| | over 'ref const(T) t'
|
| |
|
| auto ref T t | Use only when mutation of t required
and |
| | want support of by value for rvalues
|
| | (May be obviated in the long run)
|
| |
|
| auto ref const(T) t | Indicate t will not be changed,
accepts |
| | [lr]value {T, const(T), immutable(T)}
|
| | (May be obviated in the long run)
|
| |
|
| auto ref immutable(T) t | No need - restrictive with no benefit
|
| | over 'auto ref const(T) t'
|
| |
|
| T* t | Use only when mutation of t is
required. |
| | Prefer 'ref T t' unless null is
|
| | significant or dealing with unsafe
code. |
| |
|
| const(T)* t | Prefer 'ref const(T) t' unless
|
| | null is significant or dealing with
|
| | unsafe code
|
| |
|
| immutable(T)* t | No need - restrictive with no benefit
|
| | over 'const(T)* t'
|
| |
|
*** Parameter Type Guidelines ***
- Use pointers when null has specific intended meaning or the
function wants unsafe code, otherwise prefer ref
- Prefer const(T|V) to immutable(T|V) because const(T|V) is
accepting of mutables and it ensures they are not mutated.
immutable(T|V), on the other hand, only accepts immutables for
types with aliasing and therefore makes the function less
applicable. This eliminates 7 rows from consideration.
- Always use const(T|V) when passing by ref if referred to
instance is not mutated. For the non template case (i.e. V) not
using const(V) means const and immutables can not be used as
arguments. This unnecessarily reduces the application of the
function. This is a debatable guideline for template types T,
since the T being parameterized could be T = const(S), so it does
not prevent the function from being called with const(S) or
immutable(S). But the real problem with using just T instead of
const(T) in the signature of a function that does not mutate t is
the developer reading the signature has no way of knowing that T
will not be mutated without compiling and seeing if it breaks. It
is as if important user information is missing. So 'foo(T)(T t)'
or 'foo(T)(ref T t)' may both accept 'const(S) s', but only if
the compiled code does not mutate s. But without showing that
guarantee to the compiler and developer with signature like
'foo(T)(const(T) t)' or 'foo(T)(ref const(T) t)' you can be
setting yourself up for future problems. For example, if you go
with 'foo(T)(ref T t)', in the future you might (accidentally)
add a mutating call on t. Then all existing code that passed in
const(S) would break and if you test with only mutables you might
not see the errors. An example from phobos that violates this is
formatValue when passing in a struct. Even though the argument is
not modified (why would it be) the signature is has 'auto ref T
val' instead of 'auto ref const(T) val'.
- Prefer 'ref' to by value on all template parameters that are
not primitives, dynamic arrays or associative arrays - since
there is no knowledge of how expensive the copy will be. (this is
a guideline that is violated by SortedRange.(lowerBound,
upperBound, trisect)).
- When to use 'auto ref' template parameters: 'auto ref T t' as
a parameter says - make one or two functions with different
signatures and same body based on how it is called. If called
with rvalue use 'T t', if called with lvalue use 'ref T t'. From
this thread:
http://forum.dlang.org/thread/4F84D6DD.5090405@digitalmars.com?page=1
it sounds like this will no longer be necessary since in the
future a single signature of 'ref T t' will support both lvalue
and rvalue. So, for now it is best to start with 'ref T' instead
of 'auto ref T' unless there is really an interim need for
support of passing literals/rvalues into the function. One
downside to 'auto ref' is it has the power to combinatorially
increase the number of instantiations of each function. The
upside is it allows rvalues to be passed in until the ultimate
solution is implemented. In generic code, if you don't mind
requiring lvalues of users, don't bother with auto parameters at
all and stick with 'ref'.
More information about the Digitalmars-d-learn
mailing list