Mascota Jacar — leyendo contigo Un portátil cuyos ojos siguen el cursor mientras lees.
Desarrollo de Software

Rust 1.75 y 1.76: mejoras que se notan en el día a día

Rust 1.75 y 1.76: mejoras que se notan en el día a día

Actualizado: 2026-05-03

Rust 1.75 (diciembre de 2023) y Rust 1.76 (febrero de 2024) son el tipo de releases que los desarrolladores agradecen retrospectivamente: sin titulares llamativos, pero con mejoras que se perciben en el código real. El cambio más significativo de 1.75 es la estabilización de async fn en traits —una limitación que llevaba años forzando el uso del crate async-trait— junto a return-position impl Trait in traits. Rust 1.76 consolida con mejoras en debug info y en la ergonomía de tipos de puntero. Juntas, hacen el código async y los sistemas de bajo nivel considerablemente más limpios.

Puntos clave

  • async fn en traits ahora es estable en Rust 1.75: se elimina la dependencia del crate async-trait para el caso básico.
  • Return-position impl Trait en traits completa el soporte para tipos de retorno opacos sin boxing.
  • Rust 1.76 introduce pointer::byte_add y métodos relacionados para aritmética de punteros más expresiva.
  • Las mejoras de debug info en 1.76 producen backtraces más precisos para código con inlining y async.
  • Los tiempos de compilación incremental siguen mejorando release a release, con ganancias del 5-10% en proyectos grandes.

Rust 1.75: async fn en traits

La imposibilidad de declarar async fn directamente en un trait era una de las limitaciones más frustrantes del ecosistema Rust para quien venía de otros lenguajes. La causa técnica es la que impuso el compromiso: las funciones async retornan un tipo opaco (impl Future) que puede variar en tamaño según la implementación, lo que rompe el modelo de traits del compilador para tipos de tamaño estático.

La solución estabilizada en 1.75 usa desugar implícito: el compilador convierte async fn en el trait en un associated future type automáticamente. Para el caso sin dyn, esto funciona sin boxing:

rust
trait AsyncDatabase {
    async fn query(&self, sql: &str) -> Result<Vec<Row>, DbError>;
    async fn execute(&self, sql: &str) -> Result<u64, DbError>;
}

struct Postgres { /* ... */ }

impl AsyncDatabase for Postgres {
    async fn query(&self, sql: &str) -> Result<Vec<Row>, DbError> {
        // implementación concreta
        todo!()
    }

    async fn execute(&self, sql: &str) -> Result<u64, DbError> {
        todo!()
    }
}

Antes de 1.75, esto requería #[async_trait] del crate homónimo, que hace boxing de los futuros y añade overhead de heap allocation en cada llamada. Para código de alto rendimiento —exactamente el caso de uso donde más se usa Rust— ese overhead era inaceptable.

La limitación que permanece: async fn en trait no funciona directamente con dyn Trait. Para dispatch dinámico con traits async, se sigue necesitando boxing explícito o el crate async-trait. Esto cubre la mayoría de los casos prácticos, pero no todos.

Return-position impl Trait en traits

La misma release completa otra pieza del puzzle: return-position impl Trait (RPIT) en definiciones de trait. Esto permite declarar funciones en traits que retornan tipos que implementan un trait, sin necesitar boxing o associated types explícitos:

rust
trait Container {
    fn iter(&self) -> impl Iterator<Item = i32>;
    fn into_sorted(self) -> impl Iterator<Item = i32>
    where
        Self: Sized;
}

struct MyVec(Vec<i32>);

impl Container for MyVec {
    fn iter(&self) -> impl Iterator<Item = i32> {
        self.0.iter().copied()
    }

    fn into_sorted(self) -> impl Iterator<Item = i32>
    where
        Self: Sized,
    {
        let mut v = self.0;
        v.sort();
        v.into_iter()
    }
}

Antes de 1.75, este patrón requería o bien un associated type explícito (más verboso) o boxing con Box<dyn Iterator<Item = i32>> (con overhead de heap y pérdida de zero-cost abstractions). Para código de sistemas que produce iteradores, parsers o adaptadores, este cambio elimina una categoría entera de boilerplate.

Rust 1.76: ergonomía de punteros y mejor debug

Rust 1.76 no tiene cambios de lenguaje tan visibles como 1.75, pero incluye mejoras en dos áreas relevantes para desarrolladores de sistemas y para debugging.

Métodos de bytes en punteros: se estabilizan pointer::byte_add, pointer::byte_sub, pointer::byte_offset y variantes de offset. La diferencia con los métodos existentes (add, sub) es que los nuevos trabajan en bytes, no en múltiplos del tamaño del tipo apuntado:

rust
let arr: [u32; 4] = [1, 2, 3, 4];
let ptr: *const u32 = arr.as_ptr();

// Antes: requería cast explícito a *const u8 y vuelta
let second_byte = unsafe {
    (ptr as *const u8).add(1).read()
};

// Con 1.76: más directo y expresivo
let second_byte = unsafe {
    ptr.byte_add(1).cast::<u8>().read()
};

Para código que implementa protocolos binarios, parsers de bajo nivel o manipulación de memoria no tipada, este cambio reduce la verbosidad y los cast intermedios que oscurecen la intención.

Mejoras de debug info: 1.76 incluye trabajo sustancial en la calidad del debug info generado, con impacto en tres áreas:

  • Inlining más preciso: los backtraces muestran el frame correcto incluso cuando el compilador ha inlineado la función. Antes, las funciones inlineadas agresivamente aparecían como un frame opaco del caller.
  • Tipos en async: el debug info de futuros generados por async/await muestra mejor qué estado del estado-machine corresponde a qué punto del código fuente.
  • Interoperabilidad con DWARF: mejoras en la compatibilidad con herramientas como GDB, LLDB y perf que leen el DWARF debug info.
Ferris, el cangrejo naranja mascota de la comunidad Rust, símbolo del lenguaje cuyas versiones 1.75 y 1.76 completan el soporte async en traits y mejoran la experiencia de debugging

Tiempos de compilación: mejoras incrementales

Rust tiene fama de tiempos de compilación lentos, y las mejoras en este área son bienvenidas aunque sean modestas. Las releases 1.75 y 1.76 incluyen optimizaciones internas del compilador que, en proyectos grandes, se traducen en:

  • Compilaciones incrementales entre 5% y 10% más rápidas en proyectos con muchos proc macros.
  • Mejoras en el paralelismo interno del frontend de rustc.
  • Reducción del overhead de los check builds (compilaciones solo para verificar tipos, sin generar código).

Para proyectos de la escala de compiladores o servidores con cientos de crates, esas mejoras acumuladas tienen impacto real en los ciclos de desarrollo.

Compatibilidad y actualización

El upgrade a 1.75 o 1.76 es suave si el proyecto sigue la edición 2021 —prácticamente todos los nuevos proyectos. Los cambios de async fn en traits son aditivos: el código existente que usaba async-trait sigue compilando; puedes migrar gradualmente. Las únicas advertencias relevantes son para traits muy complejos con dyn que ya usaban workarounds manuales.

Para proyectos que también usan Rust en el contexto de edge computing industrial, donde el soporte de async sin boxing es crítico para evitar allocations en embedded, la estabilización de async fn en traits es especialmente relevante.

Conclusión

Rust 1.75 y 1.76 son un ejemplo de madurez del lenguaje: no añaden nuevas ideas, sino que completan compromisos pendientes. La estabilización de async fn en traits elimina una deuda técnica que el ecosistema llevaba pagando con el crate async-trait. Las mejoras de debug info reducen la fricción en la fase de investigación de bugs. Los métodos de bytes en punteros simplifican código de sistemas que antes requería cast manuales. Actualizadas juntas, estas dos releases hacen el async y el código de bajo nivel en Rust considerablemente más limpio y directo.

¿Te ha resultado útil?
[Total: 11 · Media: 4.3]

Escrito por

CEO - Jacar Systems

Apasionado de la tecnología, la infraestructura cloud y la inteligencia artificial. Escribe sobre DevOps, IA, plataformas y software desde Madrid.