Rust 2024 edition, dilin geriye dönük uyumsuz değişikliklerini topluca getiren en yeni edition'dır; Edition Guide'a göre Rust 1.85.0 ile yayımlandı. En görünür yenilikler şunlar: if ve while koşullarında let zincirleri, gen kelimesinin rezerve edilmesi, extern bloklarının ve bazı özniteliklerin unsafe olarak işaretlenme zorunluluğu, if let ile kuyruk ifadelerindeki geçici değerlerin daha erken bırakılması, std::env::set_var fonksiyonunun unsafe hale gelmesi ve Cargo tarafında rust-version bilen bağımlılık çözücüsü. Mevcut bir projeyi taşımak için önce cargo fix --edition çalıştırır, ardından Cargo.toml içinde edition = "2024" yazarsınız.

Bu yazıda her değişikliği Rust 1.99.1 ile hem 2021 hem 2024 edition'da derleyip gerçek çıktılarla gösteriyoruz ve küçük bir projeyi baştan sona geçiriyoruz. Rust'ın genel tanıtımı için Rust nedir yazısına, araçların kurulumu için Rust kurulumu: rustup ve Cargo rehberine bakabilirsiniz. Cargo'nun son sürümlerindeki araç değişiklikleri ise ayrı bir konu olduğu için Cargo son sürüm değişiklikleri yazısında ele alınıyor.

Edition nedir, Rust sürümünden farkı ne?

Rust sürümleri (1.85, 1.99 gibi) altı haftada bir çıkar ve kararlı bir özelliği bir daha kırmama sözü verir. Ancak bazen yeni bir anahtar kelime eklemek gibi uyumsuz değişiklikler gerekir. Edition mekanizması bu değişiklikleri isteğe bağlı hale getirir: her crate hangi edition ile derleneceğini kendisi seçer ve seçmediği sürece eski kurallarla derlenmeye devam eder. Edition Guide, async anahtar kelimesinin yalnızca 2018 ve sonrası seçildiğinde anahtar kelime sayılmasını buna örnek gösterir.

En önemli kural, farklı edition'lardaki crate'lerin birlikte sorunsuz çalışmasıdır. 2021 edition'da kalan bir kütüphaneyi 2024 edition'daki uygulamanızda kullanabilirsiniz; geçiş kararı her crate için özeldir ve ekosistemi bölmez. Bu yüzden edition değişiklikleri yüzeyseldir: derleyici hangi edition olursa olsun kodu aynı iç temsile indirger. Edition, Cargo.toml dosyasındaki edition anahtarıyla seçilir. Bu anahtar hiç yoksa Cargo 2015 edition'ı varsayar. cargo new ise en yeni kararlı edition'ı yazar; Cargo 1.99.1 ile oluşturduğumuz projede bu değer 2024 çıktı.

toml
[package]
name = "araclar"
version = "0.1.0"
edition = "2024"

Doğrudan rustc ile derlerken edition'ı --edition 2024 bayrağıyla verirsiniz. Bu yazıdaki tüm tek dosyalık örnekleri bu bayrakla, karşılaştırma gereken yerlerde ayrıca --edition 2021 ile derledik.

Değişikliklerin özet tablosu

DeğişiklikNe olduGeçişte yardımcı olan lint veya araç
let zincirleriif ve while koşulunda && ile birden çok letGerekmez, yalnızca yeni olanak
gen anahtar kelimesigen artık rezerve, tanımlayıcı olamazkeyword_idents_2024 (r#gen yapar)
Rezerve sözdizimi#"foo"# ve ## gibi diziler hatarust_2024_guarded_string_incompatible_syntax
if let geçici kapsamıGeçici değer else dalından önce bırakılırif_let_rescope
Kuyruk ifadesi geçicileriBloğun yerel değişkenlerinden önce bırakılırtail_expr_drop_order (yalnızca uyarı)
unsafe externextern blokları unsafe extern olmalımissing_unsafe_on_extern
unsafe özniteliklerno_mangle, export_name, link_section için unsafe(...)unsafe_attr_outside_unsafe
unsafe fn gövdesiunsafe işlemler için ayrıca unsafe blokunsafe_op_in_unsafe_fn (uyarı)
static mut referanslarıstatic mut için & ve &mut varsayılan olarak hatastatic_mut_refs (deny)
PreludeFuture ve IntoFuture prelude içinderust_2024_prelude_collisions
Box<[T]>.into_iter()Referans yerine değer üretirboxed_slice_into_iter
set_var, remove_varunsafe fonksiyon oldudeprecated_safe_2024
Cargo resolveredition 2024, resolver 3 demektirOtomatik geçiş yok
Cargo.toml anahtar adları[project], dev_dependencies gibi eski adlar kaldırıldıcargo fix --edition düzeltir

Tablodaki lint'lerin çoğu rust-2024-compatibility grubundadır ve cargo fix --edition çalıştırıldığında otomatik uygulanır. Aşağıda her birini sırayla açıyoruz.

Dil değişiklikleri

Dil değişiklikleri: let zincirleri: if ve while koşullarında birden çok let, gen artık rezerve bir anahtar kelime, if let içindeki geçici değerler daha erken bırakılıyor, Kuyruk...

let zincirleri: if ve while koşullarında birden çok let

2024 edition'dan önce let yalnızca if let veya while let biçiminde, koşulun hemen başında kullanılabiliyordu. Artık koşul, && ile bağlanmış birden çok let ifadesi ve sıradan bool ifadelerinin karışımı olabilir. İç içe if let yazmaya gerek kalmaz. Sınır şudur: let ifadeleri koşulun en üst düzeyinde durmalıdır; || ile bağlamak veya parantez içine almak hâlâ yasaktır.

rust
fn ilk_iki_toplam(sayilar: &[u8]) -> Option<u8> {
    let mut it = sayilar.iter();
    if let Some(a) = it.next()
        && let Some(b) = it.next()
        && *a > 0
    {
        a.checked_add(*b)
    } else {
        None
    }
}

fn main() {
    println!("{:?}", ilk_iki_toplam(&[10, 20, 30]));
    println!("{:?}", ilk_iki_toplam(&[7]));
    println!("{:?}", ilk_iki_toplam(&[200, 100]));
    println!("{:?}", ilk_iki_toplam(&[0, 5]));
}
// çıktı: Some(30)
// çıktı: None
// çıktı: None
// çıktı: None

Üçüncü satırdaki None, checked_add taşmayı yakaladığı için gelir; dördüncüsü ise *a > 0 koşulu sağlanmadığı için. Aynı dosyayı --edition 2021 ile derlediğimizde derleyici açıkça error: let chains are only allowed in Rust 2024 or later mesajını verdi. Edition Guide, bu özelliğin edition'a bağlı olmasının nedenini if let geçici değer kapsamının değişmesine dayandırır. Mevcut kodu bozmadığı için geçiş sırasında bir şey yapmanız gerekmez. Desen eşleştirmenin geri kalanı için match ve if let yazısına bakabilirsiniz.

gen artık rezerve bir anahtar kelime

gen kelimesi, ileride gelecek ve iterator yazmayı kolaylaştıracak gen bloklarına yer açmak için rezerve edildi. Bu yüzden 2024 edition'da gen adlı bir fonksiyon veya değişken tanımlayamazsınız:

rust
fn gen() -> u32 {
    42
}

fn main() {
    println!("{}", gen());
}
// error: expected identifier, found reserved keyword `gen`

Çözüm ham tanımlayıcı sözdizimidir: r#gen yazdığınızda ad, anahtar kelimeyle çakışmadan kullanılabilir ve kod her iki edition'da da derlenir. keyword_idents_2024 lint'i bu dönüşümü cargo fix --edition sırasında sizin yerinize yapar.

rust
fn r#gen() -> u32 {
    42
}

fn main() {
    println!("{}", r#gen());
}
// çıktı: 42

Benzer bir rezervasyon dizgi sözdiziminde de var: önek almayan korumalı dizgiler (#"foo"#) ve arada boşluk olmadan yan yana iki veya daha fazla # karakteri artık hata. Bu daha çok makro çağrılarını etkiler; rust_2024_guarded_string_incompatible_syntax lint'i gereken yerlere boşluk ekleyerek token'ların eskisi gibi ayrı okunmasını sağlar.

if let içindeki geçici değerler daha erken bırakılıyor

Bu, davranışı sessizce değiştirebilen nadir değişikliklerden biridir. if let DESEN = IFADE yazdığınızda IFADE hesaplanırken oluşan geçici değerler, 2021'de bütün if let ifadesi bitene kadar, yani else dalının sonuna kadar yaşıyordu. 2024'te ise then bloğu bittiğinde veya program else dalına girmeden hemen önce bırakılıyor. Farkı, Drop uygulayan küçük bir tiple görebilirsiniz:

rust
struct Kilit(&'static str);

impl Drop for Kilit {
    fn drop(&mut self) {
        println!("{} bırakıldı", self.0);
    }
}

impl Kilit {
    fn deger(&self) -> Option<u8> {
        None
    }
}

fn main() {
    if let Some(x) = Kilit("geçici kilit").deger() {
        println!("değer: {x}");
    } else {
        println!("else dalı çalışıyor");
    }
    println!("if let bitti");
}
// --edition 2024 çıktısı:
// çıktı: geçici kilit bırakıldı
// çıktı: else dalı çalışıyor
// çıktı: if let bitti
// --edition 2021 çıktısı:
// çıktı: else dalı çalışıyor
// çıktı: geçici kilit bırakıldı
// çıktı: if let bitti

Edition Guide'daki gerçek hayat örneği bir RwLock içerir: koşulda value.read() ile alınan okuma kilidi 2021'de else dalı boyunca tutulduğu için, o dalda value.write() ile yazma kilidi istemek kilitlenmeye yol açar. 2024'teki yeni sıra bu tuzağı ortadan kaldırır. Eski davranışa gerçekten ihtiyacınız varsa match kullanabilirsiniz; match ifadesinin geçici değerleri ifadenin sonuna kadar yaşamaya devam eder. if_let_rescope lint'i, özel Drop uygulayan bir geçici değer gördüğünde kodu match biçimine çevirmeyi önerir. Bu öneriyi körü körüne kabul etmeyin: kilit örneğinde match tam da kaçınmak istediğiniz kilitlenmeyi geri getirir.

Kuyruk ifadesindeki geçici değerler

Bir bloğun veya fonksiyonun son ifadesine kuyruk ifadesi denir. 2021'de bu ifadede oluşan geçici değerler, bloğun yerel değişkenlerinden daha uzun yaşıyordu. Bu da şaşırtıcı ödünç alma hatalarına yol açıyordu. Aşağıdaki kod 2021'de error[E0597]: `c` does not live long enough verir, 2024'te ise sorunsuz derlenir:

rust
use std::cell::RefCell;

fn uzunluk() -> usize {
    let c = RefCell::new("merhaba");
    c.borrow().len()
}

fn main() {
    println!("{}", uzunluk());
}
// --edition 2024 çıktısı:
// çıktı: 7
// --edition 2021: error[E0597]: `c` does not live long enough

Değişikliğin bir de ters yüzü var: geçici değerin kapsamı daralabildiği için 2021'de derlenen bazı kodlar 2024'te derlenmez. Edition Guide'ın örneğinde blok içinde oluşturulan bir String referansı bloğun dışında kullanılıyor:

rust
fn main() {
    let x = { &String::from("1234") }.len();
    println!("{x}");
}
// --edition 2021 çıktısı: 4
// --edition 2024: error[E0716]: temporary value dropped while borrowed

Çözüm, blok ifadesini önce bir değişkene almaktır. Referansın doğrudan bir let ile bağlandığı durumda geçici değerin ömrü uzatılır ve sonraki satırda kullanılabilir:

rust
fn main() {
    let s = { &String::from("1234") };
    let x = s.len();
    println!("{x}");
}
// çıktı: 4

Bu değişiklik için anlamı koruyan otomatik bir dönüşüm yok. tail_expr_drop_order lint'i, kuyruk ifadesinde özel Drop uygulayan bir geçici değer gördüğünde uyarı verir ama kodu değiştirmez; uyarıları tek tek incelemeniz gerekir. Bu tür hataların ayrıntısı E0597 ve lifetime hataları yazısında.

extern blokları artık unsafe extern

Yabancı fonksiyon bildirimlerinde imzanın doğru olduğunu derleyici denetleyemez; yanlış imza tanımsız davranışa yol açabilir. Bu sorumluluğu görünür kılmak için 2024'te extern bloklarının başına unsafe yazmak zorunlu. Edition Guide'a göre bu yazım Rust 1.82'den beri tüm edition'larda kullanılabiliyordu; 2024'te zorunlu oldu. Aşağıdaki kod 2021'de derlenip 5 yazar, 2024'te ise reddedilir:

rust
extern "C" {
    fn abs(x: i32) -> i32;
}

fn main() {
    let n = unsafe { abs(-5) };
    println!("{n}");
}
// error: extern blocks must be unsafe

Yeni yazımda blok içindeki her öğeyi ayrıca safe veya unsafe olarak işaretleyebilirsiniz. safe işaretli bir fonksiyon unsafe blok olmadan çağrılabilir; işaret konmayan öğeler ise varsayılan olarak unsafe kabul edilir:

rust
unsafe extern "C" {
    // abs her i32 değeriyle güvenle çağrılabilir; bunu biz taahhüt ediyoruz
    safe fn abs(x: i32) -> i32;
}

fn main() {
    let n = abs(-5);
    println!("{n}");
}
// çıktı: 5

missing_unsafe_on_extern lint'i bu anahtar kelimeyi otomatik ekler, fakat imzaların doğruluğunu kontrol etmez. Ham işaretçiler ve FFI için unsafe Rust ve ham işaretçiler yazısına bakabilirsiniz.

unsafe öznitelikler: no_mangle, export_name, link_section

Bu üç öznitelik sembol adlarını ve bağlama davranışını değiştirir. Bütün bağlanan kütüphanelerin sembolleri ortak bir ad alanında yaşadığı için yanlış kullanım, güvenli görünen kodda bile çökmeye yol açabilir. 2024'te bunların unsafe(...) içinde yazılması zorunlu:

rust
#[no_mangle]
pub extern "C" fn keydal_topla(a: i32, b: i32) -> i32 {
    a + b
}

fn main() {
    println!("{}", keydal_topla(2, 3));
}
// error: unsafe attribute used without unsafe
rust
// GÜVENLİK: bu adla başka bir global sembol tanımlanmıyor
#[unsafe(no_mangle)]
pub extern "C" fn keydal_topla(a: i32, b: i32) -> i32 {
    a + b
}

fn main() {
    println!("{}", keydal_topla(2, 3));
}
// çıktı: 5

Öznitelik üstüne neden güvenli olduğunu açıklayan bir yorum yazmak iyi bir alışkanlıktır. unsafe_attr_outside_unsafe lint'i dönüşümü yapar ama kullanımın doğru olup olmadığını yine siz denetlersiniz.

unsafe fn gövdesinde ayrı unsafe blok

Eskiden unsafe fn iki iş görüyordu: çağıranın ek şartları sağlaması gerektiğini bildiriyor ve gövdede unsafe işlemlere serbestçe izin veriyordu. İkinci rol riskli bulundu. 2024'te unsafe_op_in_unsafe_fn lint'i varsayılan olarak uyarı veriyor: gövdedeki her unsafe işlem kendi unsafe { } bloğunda olmalı.

rust
unsafe fn ilk_eleman(v: &[i32]) -> i32 {
    *v.get_unchecked(0)
}

fn main() {
    let v = [7, 8, 9];
    println!("{}", unsafe { ilk_eleman(&v) });
}
// çıktı: 7 (derleme sırasında şu uyarıyla)
// warning[E0133]: call to unsafe function `core::slice::<impl [T]>::get_unchecked` is unsafe and requires unsafe block
rust
/// # Safety
/// `v` boş olmamalıdır.
unsafe fn ilk_eleman(v: &[i32]) -> i32 {
    // GÜVENLİK: çağıran, dilimin boş olmadığını garanti ediyor
    unsafe { *v.get_unchecked(0) }
}

fn main() {
    let v = [7, 8, 9];
    println!("{}", unsafe { ilk_eleman(&v) });
}
// çıktı: 7

static mut referansları varsayılan olarak hata

static_mut_refs lint'i 2024'te deny seviyesine çıktı. Bir static mut değişkene paylaşılan veya değiştirilebilir referans almak, Rust'ın aynı anda hem paylaşım hem değişiklik yasağını küresel ölçekte düşünmenizi gerektirir; Edition Guide, referans hiç okunmasa bile kural ihlalinin anında tanımsız davranış olduğunu vurgular. println! gibi makrolar da arka planda referans aldığı için hata verir.

rust
static mut SAYAC: u32 = 0;

fn main() {
    unsafe {
        SAYAC += 1;
        let r = &SAYAC;
        println!("{r}");
    }
}
// error: creating a shared reference to mutable static

Önerilen yol, mut olmayan bir static ile iç değişebilirlik sağlayan bir tip kullanmaktır. Basit sayaçlar için atomik tipler yeterlidir; daha karmaşık durumlar için Arc, Mutex ve atomikler yazısına bakın. Ham işaretçi kaçınılmazsa derleyici &raw const ve &raw mut önerir.

rust
use std::sync::atomic::{AtomicU32, Ordering};

static SAYAC: AtomicU32 = AtomicU32::new(0);

fn main() {
    SAYAC.fetch_add(1, Ordering::Relaxed);
    SAYAC.fetch_add(1, Ordering::Relaxed);
    println!("{}", SAYAC.load(Ordering::Relaxed));
}
// çıktı: 2

Desenlerde mut, ref ve referans deseni kısıtı

Bir referansı referans olmayan bir desenle eşleştirdiğinizde (match ergonomics) varsayılan bağlama kipi ref olur. 2021'de böyle bir desenin içinde mut yazmak bu kipi sessizce move durumuna geri çeviriyordu; bu kafa karıştırıcı bulunduğu için 2024'te hata oldu. Örnekte b 2021'de i32 olarak bağlanıp 1 12 yazdırır, 2024'te ise derlenmez:

rust
fn main() {
    let dizi = &[1, 2];
    let [a, mut b] = dizi;
    b += 10;
    println!("{a} {b}");
}
// error: cannot mutably bind by value within an implicitly-borrowing pattern

Deseni tamamen açık yazmak, yani referansı & ile karşılamak sorunu çözer ve kod her iki edition'da aynı anlama gelir. Desen sözdiziminin tamamı Rust pattern sözdizimi yazısında.

rust
fn main() {
    let dizi = &[1, 2];
    let &[a, mut b] = dizi;
    b += 10;
    println!("{a} {b}");
}
// çıktı: 1 12

Makrolar, never tipi ve dönüş tipi yakalama kuralları

macro_rules! içindeki expr parça belirteci 2024'te const { ... } bloklarını ve _ ifadesini de eşleştiriyor. Aşağıdaki makro 2021'de ikinci kuralı, 2024'te birinci kuralı seçer. Eski davranışı korumak isteyenler için expr_2021 belirteci eklendi; geçiş lint'i tüm expr kullanımlarını expr_2021 yapar, ancak çoğu durumda bu değişikliği geri alıp yeni davranışı tercih etmeniz önerilir. Makro yazımının ayrıntıları macro_rules ile makro yazma yazısında.

rust
macro_rules! ornek {
    ($e:expr) => { println!("birinci kural") };
    (const $e:expr) => { println!("ikinci kural") };
}

fn main() {
    ornek!(const { 1 + 1 });
}
// --edition 2024 çıktısı: birinci kural
// --edition 2021 çıktısı: ikinci kural

İki değişiklik daha kod örneği gerektirmeyecek kadar dar kapsamlıdır. Birincisi never tipi (!) için yedek tipin () yerine ! olmasıdır; bu konuda otomatik düzeltme yoktur, ancak etkilenecek kod derleyici tarafından tespit edilir ve never_type_fallback_flowing_into_unsafe lint'i deny seviyesine çıkmıştır. İkincisi impl Trait dönüş tiplerinin artık kapsamdaki tüm generic parametreleri, lifetime'lar dahil, örtük olarak yakalamasıdır; daha dar yakalama isterseniz use<..> sınırı yazarsınız. Bir makro kuralında parça belirteci olmayan metadeğişken hatası ise Edition Guide'a göre önce yalnızca 2024 için hata olmuş, Rust 1.89 ile tüm edition'larda hataya dönüşmüştür.

Standart kütüphane değişiklikleri

Prelude, her modüle otomatik içe aktarılan öğeler kümesidir. 2024 prelude'una std::future::Future ve std::future::IntoFuture eklendi. Kendi trait'inizde poll adlı bir metot varsa ve bu metodu bir future üzerinde çağırıyorsanız çağrı belirsizleşebilir; rust_2024_prelude_collisions lint'i bu çağrıları tam nitelikli sözdizimine çevirir. Asenkron programlamanın temelleri için async/await ve Future yazısına bakabilirsiniz.

Box<[T]> üzerinde .into_iter() çağrısının anlamı da değişti. Edition Guide'a göre IntoIterator uygulaması Rust 1.80 ile tüm edition'lara eklendi, ama metot çağrısı biçimi eski edition'larda geriye uyumluluk için hâlâ referans döndüren iterator'e gidiyor. 2024'te ise değerlerin kendisini üretiyor:

rust
use std::any::type_name_of_val;

fn main() {
    let kutu: Box<[u32]> = vec![1, 2, 3].into_boxed_slice();
    for x in kutu.into_iter() {
        println!("{}", type_name_of_val(&x));
        break;
    }
}
// --edition 2024 çıktısı: u32
// --edition 2021 çıktısı: &u32 (ve boxed_slice_into_iter uyarısı)

Son olarak std::env::set_var, std::env::remove_var ve Unix'e özgü CommandExt::before_exec artık unsafe. Bazı platformlarda süreç ortamının işlenme biçimi nedeniyle bu fonksiyonları çok iş parçacıklı bir programda çağırmak güvenli değil. 2024'te çağrı bir unsafe blok ister:

rust
use std::env;

fn main() {
    env::set_var("UYGULAMA_MODU", "test");
    println!("{:?}", env::var("UYGULAMA_MODU"));
}
// error[E0133]: call to unsafe function `set_var` is unsafe and requires unsafe block
rust
use std::env;

fn main() {
    // GÜVENLİK: program bu noktada tek iş parçacığıyla çalışıyor
    unsafe { env::set_var("UYGULAMA_MODU", "test") };
    println!("{:?}", env::var("UYGULAMA_MODU"));
}
// çıktı: Ok("test")

unsafe blok eklemek yalnızca derleme hatasını kapatır; asıl iş, çağrının başka hiçbir iş parçacığı çalışmıyorken yapıldığından emin olmaktır. Mümkünse ortam değişkenini değiştirmek yerine değeri parametre olarak taşıyın. Ortam değişkenlerini okumak için komut satırı argümanları ve ortam değişkenleri yazısına bakabilirsiniz.

Cargo, rustdoc ve rustfmt tarafı

edition = "2024" yazmak resolver = "3" anlamına gelir. Bu çözücü, bağımlılık sürümü seçerken paketinizin rust-version değerini dikkate alır ve bu sürümle uyumsuz bağımlılık sürümlerine yalnızca başka seçenek kalmadığında başvurur. Çözücü ayarı çalışma alanı genelindedir; sanal bir workspace kullanıyorsanız [workspace] tablosunda resolver alanını ayrıca yazmanız gerekir. Bu geçiş için otomatik araç yoktur.

Cargo.toml içinde aynı şeyi iki farklı biçimde yazmaya izin veren eski adlar 2024'te kaldırıldı: [project] yerine [package], dev_dependencies yerine dev-dependencies, build_dependencies yerine build-dependencies, default_features yerine default-features, crate_type yerine crate-type, proc_macro yerine proc-macro. Denediğimizde 2021 projesinde Cargo bunu uyarı olarak bildirdi, 2024'te ise manifest hiç okunmadı:

bash
$ cargo check
error: failed to parse manifest at `...\anahtar\Cargo.toml`

Caused by:
  `dev_dependencies` is unsupported as of the 2024 edition; instead use `dev-dependencies`

Workspace'ten miras alınan bir bağımlılıkta, workspace tarafı varsayılan özellikleri açık bırakıyorsa paket tarafında default-features = false yazmak da artık hata; zaten etkisi yoktu. Rustdoc'ta doctest'ler tek bir ikili dosyada birleştirilerek çalıştırılıyor, bu da test süresini kısaltır. Rustfmt ise artık style edition kavramını destekliyor: cargo fmt, Cargo.toml içindeki edition'ı kullanır; isterseniz rustfmt.toml içinde style_edition = "2024" ile ayrıca belirleyebilirsiniz. Ayrıntılar için Cargo.toml ve bağımlılıklar, rustdoc ve doc test ve clippy ve rustfmt yazılarına bakın.

Adım adım geçiş: küçük bir projeyi 2024'e taşımak

Edition Guide'ın önerdiği sıra beş adımdır: bağımlılıkları cargo update ile güncellemek, cargo fix --edition çalıştırmak, Cargo.toml içindeki edition değerini değiştirmek, cargo build veya cargo test ile doğrulamak ve son olarak cargo fmt çalıştırmak. Bu sırayı, birkaç 2024 uyumsuzluğunu bilerek içeren bir 2021 projesinde denedik:

rust
extern "C" {
    fn abs(x: i32) -> i32;
}

fn gen(n: i32) -> i32 {
    unsafe { abs(n) * 2 }
}

fn main() {
    std::env::set_var("MOD", "deneme");
    let kutu: Box<[i32]> = vec![-3, 4].into_boxed_slice();
    for x in kutu.into_iter() {
        println!("{}", gen(*x));
    }
    println!("{:?}", std::env::var("MOD"));
}
// çıktı: 6
// çıktı: 8
// çıktı: Ok("deneme")

Proje sürüm kontrolü altında olmadığı için --allow-no-vcs bayrağını ekledik. Gerçek bir projede önce değişikliklerinizi commit etmeniz daha güvenlidir; böylece aracın yaptığı her değişikliği ayrı ayrı görebilirsiniz.

bash
$ cargo fix --edition --allow-no-vcs
   Migrating Cargo.toml from 2021 edition to 2024
    Checking gecis v0.1.0 (...)
   Migrating src\main.rs from 2021 edition to 2024
       Fixed src\main.rs (5 fixes)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.23s

Araç beş düzeltme yaptı. Sonuç aşağıda; dikkat ederseniz into_iter() çağrısı eski davranışı korumak için iter() oldu, set_var çağrısının üstüne de denetlemeniz gerektiğini söyleyen bir FIXME yorumu eklendi:

rust
unsafe extern "C" {
    fn abs(x: i32) -> i32;
}

fn r#gen(n: i32) -> i32 {
    unsafe { abs(n) * 2 }
}

fn main() {
    // FIXME: Audit that the environment access only happens in single-threaded code.
    unsafe { std::env::set_var("MOD", "deneme") };
    let kutu: Box<[i32]> = vec![-3, 4].into_boxed_slice();
    for x in kutu.iter() {
        println!("{}", r#gen(*x));
    }
    println!("{:?}", std::env::var("MOD"));
}

Önemli bir ayrıntı: cargo fix --edition kodu iki edition'da da derlenecek hale getirir ama Cargo.toml içindeki edition değerine dokunmaz. Denememizde komuttan sonra değer hâlâ 2021 idi. Değeri elle 2024 yaptıktan sonra cargo run aynı 6, 8 ve Ok("deneme") çıktısını verdi, cargo fmt --check ise biçim farkı bulmadı.

Sık yapılan hatalar

Sık yapılan hatalar: Önce edition değerini değiştirmek, if_let_rescope önerisini incelemeden kabul etmek, expr_2021 dönüşümünü olduğu gibi bırakmak, set_var için yalnızca unsafe...
  • Önce edition değerini değiştirmek: edition = "2024" yazıp sonra cargo fix --edition çalıştırmak sırayı bozar. Önce kodu düzeltin, sonra edition değerini değiştirin.
  • if_let_rescope önerisini incelemeden kabul etmek: Araç if let ifadesini match ile değiştirerek eski bırakma sırasını korur. Kilit içeren kodda bu, düzeltilmek istenen kilitlenmeyi geri getirebilir; her değişikliği okuyun.
  • expr_2021 dönüşümünü olduğu gibi bırakmak: Makrolarınız const blokları veya _ ile çakışmıyorsa yeni expr davranışını korumak genellikle daha doğrudur.
  • set_var için yalnızca unsafe eklemek: Asıl gereksinim, çağrı anında başka iş parçacığı olmamasıdır. Yalnızca derleyiciyi susturmak sorunu çözmez.
  • Sanal workspace'te resolver alanını unutmak: Kök paketi olmayan bir workspace'te resolver = "3" otomatik gelmez.
  • static mut hatasını allow ile kapatmak: Lint'i bastırmak tanımsız davranış riskini ortadan kaldırmaz; atomik tip veya kilit gibi bir alternatife geçin.

Sık Sorulan Sorular

2024 edition'a geçmek zorunda mıyım?

Hayır. Edition'lar isteğe bağlıdır ve yeni Rust sürümleri eski edition'lardaki kodu derlemeye devam eder. Ancak let zincirleri gibi bazı yeni olanaklar yalnızca 2024 edition'da açıktır; yeni projeler için cargo new zaten 2024 seçer.

2021 edition'daki bir kütüphaneyi 2024 projemde kullanabilir miyim?

Evet. Edition Guide'ın temel kuralı, farklı edition'lardaki crate'lerin sorunsuz birlikte çalışmasıdır. Her bağımlılık kendi edition kurallarıyla derlenir.

cargo fix bütün sorunları çözer mi?

Çoğunu çözer ama hepsini değil. Kuyruk ifadesi değişikliği, resolver değişikliği ve never tipi değişikliği için otomatik dönüşüm yoktur; araç yalnızca uyarı verir. extern imzalarının ve set_var çağrılarının doğruluğunu da sizin denetlemeniz gerekir.

Bir değişikliği tüm projeyi geçirmeden nasıl görebilirim?

Edition Guide her değişiklik için ilgili lint'i elle açmayı önerir. Örneğin crate kökünüze #![warn(if_let_rescope)] veya #![warn(keyword_idents_2024)] ekleyip cargo check çalıştırırsanız, etkilenen yerleri kodu değiştirmeden listeleyebilirsiniz.

Marka adları sahiplerine aittir. Bu yazı bağımsız bir teknik rehberdir; adı geçen kuruluşlarla bayilik veya ortaklık ilişkisi bulunduğu anlamına gelmez.

İlgili içerikler: Rust öğrenme rehberi · Cargo son sürüm değişiklikleri · Cargo.toml ve bağımlılıklar · unsafe Rust · match ve if let · Lifetime hataları