Minden hatodik hétben új stabil Rust érkezik, és ez a ritmus most sem tört meg: 2023. március 9-én kiadták a Rust 1.68-at. Elsőre talán csendesebb release-nek tűnik, mint amikor GATs-ot vagy let-else-t kaptunk, de a mostani két fő újdonság — a Cargo sparse registry protokollja és a pin! makró — pont azok közé tartozik, amik nem a nyelv felszínén látszanak, hanem a napi munkafolyamatot teszik simábbá.
Cargo sparse registry: végre gyorsabb cargo build
Aki már használt nagyobb Rust projektet, tudja, milyen érzés, amikor egy tiszta gépen lefuttatod a cargo build-et, és a Cargo először is leszedi a teljes crates.io indexet egy git repóból. Ez a git-alapú index több százezer crate metaadatát tartalmazza, és a klónozása bizony érezhető időbe (és sávszélességbe) kerül — főleg CI környezetben, ahol minden egyes futtatás gyakorlatilag nulláról indul.
A Rust 1.68-ban a sparse registry protokoll stabil elérhetőségűvé vált. Ahelyett, hogy a teljes indexet letöltenéd, a Cargo mostantól HTTP-n keresztül, igény szerint ("sparse", azaz szórt/ritka módon) kéri le csak azoknak a crate-eknek a metaadatait, amikre ténylegesen szükség van a függőségi gráfodban.
A sparse protokoll egyelőre nem alapértelmezett a crates.io-ra — ehhez explicit be kell kapcsolnod. A .cargo/config.toml fájlodban add hozzá a következőt:
[registries.crates-io]
protocol = "sparse"
Vagy környezeti változóval, projekt módosítása nélkül: CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse.
A gyakorlatban ez főleg CI pipeline-okon és nagyobb monorepo-kon érezhető igazán — a build előtti "index frissítés" lépés drasztikusan lerövidülhet. Saját gépi projekteknél, ahol az index már cache-elve van, kisebb a különbség, de az első cargo build élménye is sokkal barátságosabb lesz azoknak, akik most csatlakoznak egy csapathoz.
A csapat jelezte, hogy hosszabb távon a sparse protokoll lesz az alapértelmezett a crates.io-hoz — ez egyelőre terv, nem jelen idejű változás, szóval addig is érdemes manuálisan bekapcsolni, ha CI-t optimalizálnál.
A pin! makró: kevesebb boilerplate az async kódban
Ha valaha próbáltál stack-en pinnelt Future-t létrehozni kézzel unsafe nélkül, tudod, hogy ez korábban meglehetősen kényelmetlen volt. A Pin típus és a hozzá tartozó szabályok fontosak az async Rust működéséhez, de a helyes, biztonságos pinnelés megírása gyakran extra crate-eket (pl. pin-project vagy pin-utils) igényelt.
A Rust 1.68 stabilizálja a std könyvtárban a std::pin::pin! makrót, amivel egyszerűen, biztonságosan hozhatsz létre egy stack-re pinnelt értéket, external crate nélkül:
use std::pin::pin;
use std::future::Future;
async fn valami_lassu() -> u32 {
42
}
fn main() {
// Csak illusztráció: valódi futtatáshoz executor kell,
// pl. tokio vagy egy egyszerű block_on.
let fut = valami_lassu();
let pinned = pin!(fut);
// A `pinned` mostantól Pin<&mut impl Future<Output = u32>>
// és tovább adható polling célra.
let _: Pin<&mut _> = pinned;
}
A pin! makró lényegében azt csinálja, amit korábban a pin_utils::pin_mut! vagy hasonló segédmakrók: egy lokális változót hoz létre, és azt helyben pinneli, elkerülve a heap allokációt (amit egy Box::pin igényelne). Ez különösen hasznos, ha saját executor-t írsz, vagy alacsony szintű async kódot fejlesztesz, ahol minden allokáció számít.
use std::pin::pin;
use std::future::Future;
use std::task::{Context, Poll};
fn poll_once<F: Future>(fut: F) -> Poll<F::Output> {
let mut fut = pin!(fut);
// Egyszerűsített, csak demonstrációs célú waker nélkül nem futtatható éles kódban,
// de jól mutatja, hogyan válik elérhetővé a Pin<&mut F> egy async fn-ből.
let _ = &mut fut;
Poll::Pending
}
A fenti második példa didaktikus, valódi pollinghoz megfelelő Waker-re és Context-re van szükség — éles kódban használj executor-t (pl. tokio::main), ne kézzel pollingolj Future-öket, hacsak nem pont ez a feladatod (executor írás).
Mit jelent ez a mindennapi munkában?
Ha nem írsz saját async runtime-ot vagy executor-t, a pin! makró közvetlen hatása valószínűleg kicsi lesz a napi kódodban — a legtöbb alkalmazásfejlesztő ritkán nyúl közvetlenül Pin-hez, hiszen a tokio és hasonló runtime-ok ezt már elrejtik előlünk. Ahol viszont igazán érezhető a különbség, az a build- és CI-idő: a sparse registry protokoll bekapcsolása gyakorlatilag ingyen kapott sebességnövekedés, különösen ha GitHub Actions-ön vagy más felhős CI-n futtatod a teszteket, és minden egyes futásnál újra le kellene húzni a teljes crates.io indexet.
Érdemes tehát:
- ha CI-t üzemeltetsz, próbáld ki a
protocol = "sparse"beállítást, és mérd meg a build időt előtte-utána; - ha alacsony szintű async kódot írsz (pl. saját
Futureimplementációt), nézz rá apin!makróra — lehet, hogy kiválthatsz vele egy külső függőséget.
Hogyan frissíts?
A frissítés a megszokott módon zajlik, a rustup segítségével:
rustup update stable
Ez letölti és beállítja az 1.68-as toolchaint, ha még nem lenne meg. Ellenőrizni a verziót így tudod:
rustc --version
# rustc 1.68.0 (2c8cc3432 2023-03-06)
A cargo.lock fájlok és a meglévő projektjeid teljesen kompatibilisek maradnak — a Rust csapat szigorúan tartja magát a visszafelé kompatibilitási ígéretéhez, szóval a frissítés gyakorlatilag mindig kockázatmentes.
Összefoglalás
A Rust 1.68 nem egy hatalmas, nyelvi paradigmaváltó release, hanem inkább a mindennapi fejlesztői élményt csiszoló verzió. A sparse registry protokoll komoly build- és CI-gyorsulást hozhat, ha bekapcsolod, a pin! makró pedig egyre kevésbé teszi szükségessé külső crate-ek bevonását az alacsony szintű async munkához. Ha eddig még nem tetted, most van itt az ideje egy rustup update stable-nek — a frissítés, mint mindig, most is fájdalommentes.