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 egycfgfeltétel soha nem teljesülhet, vagy ha elgépelted egy feature nevét. std::io::Error::otherstabilizálva — kényelmesebb módja tetszőleges hibaértékio::Error-ba csomagolásának.- Kisebb, de hasznos módosítás a
mainszál stack méretének kezelésében aRUST_MIN_STACKkö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.
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);
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).
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:
- Futtasd le a
cargo updateésrustup update stablepárost, majd nézd át, hogy a CI pipeline-od is az új toolchaint használja-e. - 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 tisztaconst fnformába. - 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 aCargo.tomlfeature listáját a kódban használtcfgpredikátumokkal. - Ha
OnceCellvagyOnceLockalapú 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.