Naming convention for functions that mutate arguments vs creating a new object
https://softwareengineering.stackexchange.com/questions/422786
-
23-03-2021 - |
Вопрос
Take the following signature
BigInt* addBigInt(BigInt* arg1, BigInt* arg2);
Traditionally, the safest way to implement this function would be for it not mutate the arguments, and would necessarily need to create a new object to result for the operation. Alternatively, if the aim is to conserve on system memory, arg1
could be the object that the resulting operation would return having mutated that object.
The question is, is there an established naming convention the would delineate the nuance between the two implementation choices? Or is this something that would need to be clearly laid out in the documentation where the developer is establishing the convention?
Let's assume this is in C where function overloading is not possible.
Решение
If you create a new object, return it.
public int[] square(int a[], int size)
{
int b[] = malloc(size * sizeof(int));
for (int i=0; i<size; i++)
b[i] = a[i] * 2;
return b;
}
If you don't create an object, return void
.
public void square(int a[], int size)
{
for (int i=0; i<size; i++)
a[i] = a[i] * 2;
}
Note that programming languages that support method overloading don't usually distinguish between return types. If you really need both functions, just change the name of one of them; perhaps square
and square_copy
.
Другие советы
Now I have never thought about trying to find naming convention about that. But If the method is void we can speculate that it may be a Procedure.
If I am to name a procedure I would focus more on describing what it does. In my opinion, it is more common for a Procedure to do more than one thing compared to a function and then a procedure would benefit from a more descriptive name even if it does not start with a verb.
In C, you'd use const
to indicate that a pointer argument is not mutable. Using your addBigInt
example, if you want to indicate that neither *arg1
nor *arg2
are mutable, you'd write
BigInt *addBigInt( const BigInt *arg1, const BigInt *arg2 );
Any attempt to write to *arg1
or *arg2
in addBigInt
should cause the compiler to issue a diagnostic (even if the actual arguments arg1
and arg2
point to are not const
). If you were going to write the function such that the result is stored in *arg1
, you could do something like
BigInt *addBigInt( BigInt *arg1, const BigInt *arg2 );
You'd just return arg1
rather than a new object. However, that's not perfectly clear just from the signature - you'd want to add some documentation:
/**
* Adds two BigInts together, result is stored in the object
* pointed to by arg1
*
* Inputs:
* arg1
* arg2
*
* Outputs:
* arg1
*
* Return value:
* arg1
*/
Note that const
doesn't necessarily mean "put this thing in read-only memory" (although that may be the result), it just means "this thing is not meant to be written to, and flag any code that attempts to do so."
Basic rules of const
and pointers:
const T *p; // p is a non-const pointer to const T; you can write to p, but not *p
T const *p; // same as above
T * const p; // is a const pointer to non-const T; you can write to *p, but not p
const T * const p; // p is a const pointer to const T; you cannot write to either p or *p
T const * const p; // same as above
If you want to make it super explicit that a function modifies its argument(s), you might name it inplace_something()
or something_inplace()
so the intention becomes apparent without looking at argument or result types which may not be immediately visible in the context of the call.