Ma jelent meg a Rust 1.72.0, és ahogy megszoktuk, a rustup update stable egy paranccsal felhozza a toolchaint a legfrissebb verzióra. Ez a release nem hoz látványos szintaktikai újítást, viszont két olyan finomítást tartalmaz, amivel nap mint nap találkozhatsz, ha compile-time számításokat írsz, vagy ha a #[cfg(...)] attribútumokkal dolgozó, feature-flag-es projekteket karbantartasz.

A legfontosabb újítások röviden

A teljesség kedvéért, mielőtt rátérnénk a két fő témára, gyorsan összeszedem, mi minden változott az 1.72-ben:

  • Eltűnt a const-eval lépésszám limit — erről mindjárt bővebben.
  • Jobb hibaüzenetek #[cfg] attribútumoknál — a compiler most pontosabban jelzi, ha egy cfg feltétel soha nem teljesülhet, vagy ha elgépelted egy feature nevét.
  • std::io::Error::other stabilizálva — kényelmesebb módja tetszőleges hibaérték io::Error-ba csomagolásának.
  • Kisebb, de hasznos módosítás a main szál stack méretének kezelésében a RUST_MIN_STACK környezeti változóval kapcsolatban.
  • A szokásos rakás library stabilizáció és teljesítményjavítás a fordítóban.

Most nézzük meg alaposabban a két legérdekesebb pontot.

A const-eval lépésszám limit háttere

Ha valaha próbáltál const fn-ben komolyabb számítást futtatni — mondjuk egy nagyobb táblázat generálását fordítási időben, vagy egy rekurzív algoritmus kiértékelését —, valószínűleg belefutottál már ebbe a hibába:

error: evaluation of constant value failed
= note: this error originates in the macro...
= note: `#[deny(long_running_const_eval)]` on by default

vagy a klasszikus, régebbi formájában:

error[E0080]: reached the configured maximum number of steps

A const evaluator (ami a fordítási idejű kódot futtatja) korábban egy fix, viszonylag alacsony lépésszám-limithez volt kötve. Ez a limit arra szolgált, hogy megvédjen egy tipikus fejlesztői gépet attól, hogy egy véletlenül végtelen ciklusba futó const fn órákra beragasszon a fordítás közben. A gond az volt, hogy ez a limit teljesen arbitrer volt: egy egyszerű, de sok iterációt igénylő algoritmus (mondjuk egy nagyobb prímszita, vagy egy hash-tábla felépítése fordítási időben) simán nekiment a limitnek, miközben tényleg csak néhány másodpercet vett volna igénybe.

A Rust 1.72-től ez a hardkódolt lépésszám-limit megszűnt. Helyette a fordító most egy okosabb heurisztikát alkalmaz: ha egy const kiértékelés túl sokáig fut (ez most már inkább idő- és memóriaalapú becslés, nem egy fix számláló), a compiler figyelmeztetést ad (long_running_const_eval lint), de nem áll meg azonnal egy mesterséges korlátnál. Ez azt jelenti, hogy a te const kódod addig futhat, amíg tényleg szükséges — a compiler csak akkor szól, ha gyanúsan sokáig tart, de nem vág bele durván a lábadba egy önkényes szám miatt.

Megjegyzés

A long_running_const_eval lint alapból deny szinten van, de ha tudod, hogy a const számításod jogosan hosszú (pl. egy nagy lookup táblát generálsz), az #[allow(long_running_const_eval)] attribútummal felül tudod írni a kiértékelendő elemen.

Mit tehetsz meg most fordítási időben, amit eddig nem?

A gyakorlatban ez azt jelenti, hogy sokkal bátrabban írhatsz nehezebb const fn logikát. Nézzünk egy konkrét példát: generáljunk fordítási időben egy Fibonacci-táblázatot, ami korábban simán belefutott volna a lépésszám limitbe nagyobb N esetén.

const fn fibonacci_table<const N: usize>() -> [u64; N] {
    let mut table = [0u64; N];
    if N > 0 {
        table[0] = 0;
    }
    if N > 1 {
        table[1] = 1;
    }
    let mut i = 2;
    while i < N {
        table[i] = table[i - 1] + table[i - 2];
        i += 1;
    }
    table
}

const FIB_100: [u64; 100] = fibonacci_table();

fn main() {
    println!("A 50. Fibonacci szám: {}", FIB_100[50]);
}

Ez a kód akár 1.65-ös Rust-tal is fordult volna (a while ciklus és a const fn már régóta stabil), de minél nagyobbra vetted az N értéket, annál nagyobb eséllyel futottál bele a régi limitbe. Most, hogy a limit gyakorlatilag eltűnt, sokkal komolyabb számításokat is nyugodtan rábízhatsz a const evaluatorra — például egy statikus lookup táblát egy parserhez, vagy egy előre kiszámolt hash-értéket egy konstansból.

Érdemes megjegyezni, hogy ez remekül kombinálható a már régebb óta stabil GAT-okkal (Generic Associated Types) és a let-else szintaxissal, ha komplexebb, generikus const logikát írsz:

const fn checked_divide(a: u32, b: u32) -> u32 {
    let Some(result) = a.checked_div(b) else {
        panic!("osztás nullával fordítási időben!");
    };
    result
}

const RESULT: u32 = checked_divide(100, 4);
Tipp

Ha komolyabb const számításokat írsz, és bizonytalan vagy a futásidőben, próbáld ki lokálisan a cargo build --release-t is — a const evaluator viselkedése optimalizált build esetén is ugyanaz, de mindig jó, ha a CI-ban is leteszteled a fordítási időt, mielőtt egy nagyobb refaktort mergelsz.

Javuló hibaüzenetek cfg attribútumok esetén

A második nagy téma a #[cfg(...)] diagnosztika javulása. Ha nagyobb, feature flag-es projekten dolgozol — mondjuk egy crate-en, ami feature = "async", feature = "blocking", target_os = "linux" és hasonló kombinációkkal van teleszórva —, biztosan ismerős az a pillanat, amikor egy elgépelt feature név miatt egy egész kódblokk csendben "eltűnik" a fordításból, és te csak találgatod, miért nem működik valami.

A Rust 1.72 ezen a fronton konkrét, name-alapú javításokat hoz: ha a compiler gyanús eltérést lát egy cfg predikátumban (pl. hasonló nevű, de mégsem egyező feature vagy target érték), a hibaüzenet most pontosabban jelzi, melyik kódrészlet és melyik feltétel miatt maradt ki a fordításból, illetve unreachable/dead_code figyelmeztetéseknél is jobban látszik, hogy a cfg volt a ludas.

Nézzünk egy tipikus esetet:

#[cfg(feature = "loging")] // elgépelve: "logging" helyett
fn init_logger() {
    println!("Logger inicializálva");
}

fn main() {
    #[cfg(feature = "loging")]
    init_logger();

    println!("Alkalmazás elindult");
}

Ha a Cargo.toml-ban valójában a logging feature létezik, de a loging sosem lesz definiálva, korábban ez csendben lefordult volna, és az init_logger funkció egyszerűen sosem hívódott volna meg — semmi hiba, semmi figyelmeztetés, csak egy néma, megmagyarázhatatlan viselkedés. Az 1.72-es diagnosztikai finomítások jobban rávilágítanak arra, hogy egy cfg-vel védett kódrész miért maradt ki, különösen ha a környező kódban egyértelmű az inkonzisztencia (pl. ha máshol helyesen van írva a feature neve).

Figyelem

A cfg-diagnosztika javulása nem jelenti azt, hogy a compiler most már validálja a feature neveket a Cargo.toml alapján — ehhez továbbra is külön eszközre (pl. cargo hack check-cfg, vagy a Cargo saját unexpected_cfgs lintje, ami más release-eken keresztül fejlődik) van szükség. Az 1.72 "csak" a meglévő diagnosztikai üzenetek pontosságát és kontextusát javítja.

Frissítési tanácsok meglévő projektekhez

A jó hír, hogy ez a release teljesen visszafelé kompatibilis — nincs semmi breaking change, amivel foglalkoznod kellene. Néhány praktikus tanács a frissítéshez:

  1. Futtasd le a cargo update és rustup update stable párost, majd nézd át, hogy a CI pipeline-od is az új toolchaint használja-e.
  2. Ha korábban belefutottál a const-eval lépésszám limitbe, és ezért kerülő megoldásokat építettél be (pl. runtime-ra tetted át a számítást, vagy build.rs-ben generáltad a kódot), most érdemes megnézni, vissza tudod-e vinni ezt a logikát tiszta const fn formába.
  3. Nézd át a #[cfg] attribútumaidat — most jobb eséllyel kapsz értelmes hibaüzenetet, ha valahol elgépelted egy feature nevet, de érdemes proaktívan is átfutni a Cargo.toml feature listáját a kódban használt cfg predikátumokkal.
  4. Ha OnceCell vagy OnceLock alapú lazy inicializációt használsz (ezek már korábban stabilizálódtak), ez a release nem hoz nekik változást, de jó alkalom lehet átnézni, hogy tényleg a legfrissebb, std-beli megoldást használod-e, nem valamelyik külső crate-et.

Összefoglalás

A Rust 1.72 nem egy azok közül a release-ek közül, amiket a Twitter/X tele fog szórni izgalmas demókkal, de a const-eval lépésszám limit eltávolítása és a cfg-diagnosztika javulása pontosan azok a "csendes" fejlesztések, amik hosszú távon a legtöbb frusztrációt spórolják meg. Ha gyakran írsz fordítási idejű logikát, vagy nagyobb, feature-flag-es crate-et karbantartasz, ez a két apró változás minden bizonnyal hamarosan a napi munkádban is érezhető lesz. Frissíts nyugodtan — nincs itt semmi, ami miatt óvatosnak kellene lenned.