105 / Tipos y patrones

Campo Fn en un struct

Un struct puede tener un campo de tipo Fn, y llamarlo funciona en tres formas de receptor: variable local, parámetro por valor y campo anidado dentro de otro struct. Lo que no funciona todavía es llamar el campo directo (pais.calcular_impuesto(monto)): el compilador lo rechaza con NYX1016, así que primero hay que ligarlo a una variable sin anotar el tipo.

105-struct-fn-field.nxFuente →
// Campo `Fn` en un struct: ligar el campo a una variable SIN anotar el tipo
// y llamar la variable — funciona con el receptor como variable local,
// parámetro por valor y campo anidado (también con parámetro puntero,
// `c: *Contrato`, no mostrado aquí). Llamar el campo DIRECTO
// (`pais.calcular_impuesto(monto)`) sigue sin soportarse (NYX1016): hay que
// ligarlo a una variable primero.

struct Pais {
    nombre: String,
    calcular_impuesto: Fn(float) -> float
}

struct Region {
    pais: Pais
}

fn impuesto_ar(monto: float) -> float {
    return monto * 0.21
}

fn impuesto_uy(monto: float) -> float {
    return monto * 0.22
}

// Receptor: parámetro POR VALOR
fn cobrar(p: Pais, monto: float) -> float {
    let calcular = p.calcular_impuesto   // sin anotar
    return calcular(monto)
}

fn main() -> int {
    let ar: Pais = Pais { nombre: "Argentina", calcular_impuesto: impuesto_ar }

    // Receptor: variable local
    let f = ar.calcular_impuesto
    print(ar.nombre + ": " + float_to_string(f(100.0)))

    print("cobrar(): " + float_to_string(cobrar(ar, 100.0)))

    // Receptor: campo anidado
    let uy: Pais = Pais { nombre: "Uruguay", calcular_impuesto: impuesto_uy }
    let r: Region = Region { pais: uy }
    let calcular_anidado = r.pais.calcular_impuesto
    print(r.pais.nombre + ": " + float_to_string(calcular_anidado(100.0)))

    return 0
}
Salidastdout
Argentina: 21.0
cobrar(): 21.0
Uruguay: 22.0

Cómo funciona

Las tres llamadas usan el mismo patrón: let calcular = valor.campo_fn seguido de calcular(argumentos). f = ar.calcular_impuesto liga el campo de una variable local; cobrar hace lo mismo con p, un parámetro recibido por valor; y calcular_anidado = r.pais.calcular_impuesto atraviesa un campo struct dentro de otro struct antes de ligar.

Ninguna de las tres anota el tipo de calcular: el compilador infiere la firma completa (Fn(float) -> float) desde el campo de origen, así que la llamada indirecta sabe qué convenio de retorno usar. Esto es justamente lo que evita el problema de un Fn sin firma — ver la receta de plazo y causa HTTP para el caso donde SÍ hace falta declarar la firma a mano.