Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Regla 0x2016h: Escribí el contrato de la función antes de implementarla

Funciones, contratos y modularizacion (0x20XX)

Universidad Nacional de Río Negro

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 consecuenciaEfecto concreto
Interfaz ambiguaEl llamador no sabe si debe validar antes de llamar.
Bug silenciosoCada llamador asume un comportamiento distinto ante el error.
TestingNo se sabe qué casos son obligatorios de probar.
RefactorizaciónCambiar 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

Cómo detectarla

HerramientaComandoSeñal
gaffgaff check archivo.cRegla 0x2003h (documentación estructurada).
doxygendoxygenFunciones sin bloque de documentación.
Revisión manualAusencia de @pre / @post en funciones de biblioteca.

Checklist de autocontrol

Reglas relacionadas