Regla 0x2016h: Escribí el contrato de la función antes de implementarla
Funciones, contratos y modularizacion (0x20XX)
0x2016h: Escribí el contrato de la función antes de implementarla¶
Enunciado normativo¶
DEBE redactarse el contrato de cada función —qué recibe, qué garantiza, qué devuelve y qué casos no admite— antes de escribir su cuerpo. El contrato es la especificación; el cuerpo es una implementación de esa especificación.
¿Por qué existe esta regla?¶
El problema¶
Implementar sin contrato obliga a decidir sobre la marcha cuestiones que
deberían estar resueltas: ¿qué hago si n es cero?, ¿puedo modificar el
arreglo de entrada?, ¿qué devuelvo ante un error? Cada una de esas decisiones
tomada a mitad del cuerpo produce código inconsistente entre funciones.
Escribir el contrato primero obliga a resolver esas preguntas en el momento en que son baratas: antes de tener veinte líneas comprometidas con una respuesta implícita.
Consecuencias de violarla¶
| Tipo de consecuencia | Efecto concreto |
|---|---|
| Interfaz ambigua | El llamador no sabe si debe validar antes de llamar. |
| Bug silencioso | Cada llamador asume un comportamiento distinto ante el error. |
| Testing | No se sabe qué casos son obligatorios de probar. |
| Refactorización | Cambiar la implementación puede romper supuestos no documentados. |
Fundamento en la cátedra¶
El diseño por contrato (precondición, postcondición e invariante) es central en la materia y se formaliza en 0x2003h: Todas las funciones deben incluir documentación completa y estructurada. Esta propuesta lo adelanta al momento del diseño, para que la documentación no sea un trámite posterior.
Alcance y excepciones¶
Aplica a funciones públicas y a las privadas no triviales. Una función
static de tres líneas puede documentarse con una frase. No aplica a main,
cuyo contrato lo fija el estándar.
Ejemplos exhaustivos¶
❌ Contraejemplo 1 — Implementar sin decidir el caso vacío¶
int maximo(int v[], int n)
{
int m = v[0];
for (int i = 1; i < n; i++) {
if (v[i] > m) {
m = v[i];
}
}
return m;
}Por qué falla: no se definió qué ocurre con n == 0; el cuerpo lee v[0] de
un arreglo vacío (acceso fuera de límites). Sin contrato, el error es
invisible hasta que alguien lo llama con una lista vacía.
❌ Contraejemplo 2 — Precondiciones implícitas¶
double promedio(int *datos, int cantidad);Por qué falla: la firma no dice si datos puede ser NULL, si cantidad
puede ser negativa, ni qué devuelve ante un error. Cada llamador improvisa
una política distinta.
✅ Ejemplo conforme 1 — Contrato explícito¶
/**
* @brief Calcula el máximo de un arreglo no vacío.
*
* @param v Arreglo de enteros. Precondición: no es NULL.
* @param n Cantidad de elementos. Precondición: n >= 1.
* @returns El mayor elemento de v[0..n-1].
* @pre n >= 1 y v != NULL.
* @post El arreglo v no se modifica.
*/
int maximo(const int v[], size_t n);Con el contrato escrito, la implementación sabe que puede confiar en n >= 1
y el llamador sabe que debe validar antes.
✅ Ejemplo conforme 2 — Contrato que admite el caso vacío¶
/**
* @brief Calcula el promedio de un arreglo posiblemente vacío.
*
* @param v Arreglo de enteros; puede ser NULL si n == 0.
* @param n Cantidad de elementos; puede ser 0.
* @returns El promedio, o 0.0 si n == 0.
* @post No modifica v.
*/
double promedio(const int v[], size_t n);Al decidir explícitamente que n == 0 es válido, la función no puede leer
fuera de límites y el llamador tiene una respuesta documentada.
⚠️ Casos límite¶
Funciones privadas triviales: basta una línea de comentario con el contrato.
Propiedad de memoria: si la función devuelve un puntero, el contrato debe decir quién libera (0x3006h: Documentá la propiedad de los recursos al utilizar punteros).
Efectos laterales: si modifica un parámetro de salida, debe declararlo en la postcondición.
Cómo detectarla¶
| Herramienta | Comando | Señal |
|---|---|---|
gaff | gaff check archivo.c | Regla 0x2003h (documentación estructurada). |
doxygen | doxygen | Funciones sin bloque de documentación. |
| Revisión manual | — | Ausencia de @pre / @post en funciones de biblioteca. |
Checklist de autocontrol¶
¿Escribí qué recibe y qué devuelve antes del cuerpo?
¿Decidí explícitamente el comportamiento ante la entrada vacía o inválida?
¿Documenté si el parámetro puede ser
NULL?¿Indiqué si la función modifica sus argumentos?
Reglas relacionadas¶
0x2003h: Todas las funciones deben incluir documentación completa y estructurada — documentación estructurada obligatoria.
0x3009h: Documentá explícitamente los casos en que una función puede retornar NULL — documentar retornos
NULL.0x3006h: Documentá la propiedad de los recursos al utilizar punteros — documentar la propiedad de los recursos.
0x6001h: Diseñá el algoritmo antes de escribir código en C — el contrato y el pseudocódigo se redactan juntos.