Rust'ta error[E0277], bir tipin o noktada beklenen trait'i uygulamadığını söyler: örneğin {:?} ile yazdırılan bir generic tipte Debug sınırı eksiktir, i32 ile f64 çarpılmaya çalışılmaktadır ya da ? operatörü Result döndürmeyen bir fonksiyonda kullanılmaktadır. error[E0308]: mismatched types ise beklenen tip ile verilen tipin uyuşmadığını bildirir; en sık nedenleri fonksiyon sonundaki fazladan noktalı virgül, if ve else dallarının farklı tip döndürmesi ve String beklenen yere &str vermektir. E0425 bulunamayan bir değer adını, E0433 ise içe aktarılmamış bir tip veya modülü gösterir.

Bu yazıda dört hatayı Rust 1.99.1'in gerçek mesajlarıyla, en sık karşılaşılan durumlar üzerinden inceliyoruz. Trait ve generic kavramlarının kendisi trait tanımlama ve impl ile Rust generics yazılarında, tipler arası dönüşüm From, Into ve TryFrom yazısında anlatılıyor. Sahiplik ve ödünç alma kaynaklı hatalar için E0382 ve E0502 yazılarına bakabilirsiniz.

E0277: trait bound is not satisfied

E0277: trait bound is not satisfied: Generic fonksiyonda sınır eksikse, Kendi tipinizi {} ile yazdırmak, Farklı sayı tiplerini birlikte kullanmak, Vec veya dilimi i32 ile...

Hata kodu dizinine göre E0277, belirli bir trait'i bekleyen bir yerde o trait'i uygulamayan bir tip kullandığınızda çıkar. En açık hali, kendi trait'inizi isteyen bir generic fonksiyonu bu trait'i uygulamayan bir tiple çağırmaktır:

rust
trait Kaydedilebilir {
    fn kaydet(&self) -> String;
}

fn arsivle<T: Kaydedilebilir>(oge: T) {
    println!("{}", oge.kaydet());
}

fn main() {
    arsivle(5i32);
}
// error[E0277]: the trait bound `i32: Kaydedilebilir` is not satisfied

Mesaj iki yeri birbirine bağlar: çağrıdaki 5i32 değeri için the trait `Kaydedilebilir` is not implemented for `i32` der, ardından bir not ile sınırın nerede konduğunu gösterir: required by this bound in `arsivle`. Rust 1.99.1 burada trait'in hiç uygulaması olmadığını da fark edip bir tane eklemeyi önerir. Çözüm tam olarak budur:

rust
trait Kaydedilebilir {
    fn kaydet(&self) -> String;
}

impl Kaydedilebilir for i32 {
    fn kaydet(&self) -> String {
        format!("sayı:{self}")
    }
}

fn arsivle<T: Kaydedilebilir>(oge: T) {
    println!("{}", oge.kaydet());
}

fn main() {
    arsivle(5i32);
}
// çıktı: sayı:5

Generic fonksiyonda sınır eksikse

E0277'nin ikinci yüzü, hatanın çağrıda değil fonksiyon tanımında çıkmasıdır. Aşağıdaki fonksiyon yalnızca Debug uygulayan i32 ile çağrılıyor, yine de derlenmiyor:

rust
fn yazdir<T>(deger: T) {
    println!("{:?}", deger);
}

fn main() {
    yazdir(5);
}
// error[E0277]: `T` doesn't implement `Debug`

Hata kodu dizini bunun nedenini açıklar: generic fonksiyon, olası tüm girdi tipleri için çalışmak zorundadır ve Rust yalnızca çağrılan fonksiyonun imzasına bakar. Bu yüzden her tip parametresinin ihtiyaç duyduğu tüm yetenekler imzada yazmalıdır. Derleyici T için std::fmt::Debug sınırı eklemeyi önerir. Sınırı ekledikten sonra fonksiyon Debug uygulayan her tiple çağrılabilir:

rust
use std::fmt::Debug;

fn yazdir<T: Debug>(deger: T) {
    println!("{:?}", deger);
}

fn main() {
    yazdir(5);
    yazdir("metin");
    yazdir(vec![1.5, 2.0]);
}
// çıktı: 5
// çıktı: "metin"
// çıktı: [1.5, 2.0]

Birden çok sınır gerektiğinde T: Debug + Clone biçiminde birleştirebilir veya imza uzadıysa where yan tümcesine taşıyabilirsiniz; bu yazımlar trait yazısında ayrıntılı.

Kendi tipinizi {} ile yazdırmak

println!("{}", x) biçimi tipin Display trait'ini uygulamasını ister. Kendi struct'ınız için bu trait otomatik gelmez:

rust
struct Urun {
    ad: String,
    fiyat: u32,
}

fn main() {
    let u = Urun { ad: String::from("Kalem"), fiyat: 15 };
    println!("{}", u);
}
// error[E0277]: `Urun` doesn't implement `std::fmt::Display`

Derleyici iki yol önerir: hata ayıklama çıktısı yeterliyse {:?} kullanmak (bunun için tipe #[derive(Debug)] eklemeniz gerekir) ya da kullanıcıya gösterilecek biçimi Display uygulayarak kendiniz tanımlamak. Display türetilemez, elle yazılır:

rust
use std::fmt;

#[derive(Debug)]
struct Urun {
    ad: String,
    fiyat: u32,
}

impl fmt::Display for Urun {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{} ({} TL)", self.ad, self.fiyat)
    }
}

fn main() {
    let u = Urun { ad: String::from("Kalem"), fiyat: 15 };
    println!("{}", u);
    println!("{:?}", u);
}
// çıktı: Kalem (15 TL)
// çıktı: Urun { ad: "Kalem", fiyat: 15 }

Biçimlendirme belirteçlerinin tamamı Display ve Debug formatlama, türetilebilir trait'ler derive ile Debug, Clone, PartialEq yazısında.

Farklı sayı tiplerini birlikte kullanmak

Rust sayı tiplerini kendiliğinden birbirine çevirmez. i32 ile f64 çarpmak bir tip hatası değil, trait hatası olarak raporlanır, çünkü * operatörü Mul trait'i üzerinden çalışır ve i32 için Mul<f64> uygulaması yoktur:

rust
fn main() {
    let adet: i32 = 3;
    let fiyat: f64 = 2.5;
    let toplam = adet * fiyat;
    println!("{toplam}");
}
// error[E0277]: cannot multiply `i32` by `f64`

Çözüm, bir tarafı açıkça diğer tipe çevirmektir. i32 değerinin f64 tipine dönüşümü kayıpsız olduğu için standart kütüphane From uygulaması sunar. as ile dönüşüm de mümkündür, ama kayıplı dönüşümlerde sessizce kırpma veya yuvarlama yapabildiği için kayıpsız bir yol varsa from tercih edilir:

rust
fn main() {
    let adet: i32 = 3;
    let fiyat: f64 = 2.5;
    let toplam = f64::from(adet) * fiyat;
    println!("{toplam}");
}
// çıktı: 7.5

Vec veya dilimi i32 ile indekslemek

Dilim ve vektör indeksleri usize tipindedir. Başka bir tamsayı tipiyle indekslemek E0277 verir ve mesaj bunu açıkça söyler: slice indices are of type `usize` or ranges of `usize`. Mesajdaki [{integer}], vektör elemanlarının tipinin henüz kesinleşmemiş bir tamsayı olduğunu gösterir:

rust
fn main() {
    let v = vec![10, 20, 30];
    let i: i32 = 1;
    println!("{}", v[i]);
}
// error[E0277]: the type `[{integer}]` cannot be indexed by `i32`

Negatif bir i32 değerinin geçerli bir indeks olamayacağını unutmayın. usize::try_from dönüşümü negatif değerlerde hata döndürür; bu, as usize yazmaktan daha güvenlidir. get metodu ile birleştirildiğinde sınır dışı indekslerde de panik yerine None alırsınız:

rust
fn main() {
    let v = vec![10, 20, 30];
    let i: i32 = 1;
    let j = usize::try_from(i).expect("negatif indeks");
    println!("{}", v[j]);

    let k: i32 = -1;
    println!("{:?}", usize::try_from(k).ok().and_then(|x| v.get(x)));
}
// çıktı: 20
// çıktı: None

collect ile yanlış tipte koleksiyon kurmak

collect hedef koleksiyonu tip bilgisinden seçer ve bunun için FromIterator trait'ine dayanır. Iterator'ün ürettiği eleman tipi hedefle uyuşmuyorsa sonuç yine E0277'dir. Aşağıda split metni &str parçalarına ayırır, ama hedef Vec<i32> olarak yazılmış:

rust
fn main() {
    let sayilar: Vec<i32> = "1,2,3".split(',').collect();
    println!("{sayilar:?}");
}
// error[E0277]: a value of type `Vec<i32>` cannot be built from an iterator over elements of type `&str`

Parçaları önce sayıya çevirmek gerekir. Dönüşüm başarısız olabileceği için Result değerlerinden oluşan bir iterator'ü Result<Vec<i32>, _> içine toplamak, ilk hatada duran ve hatayı döndüren düzgün bir yoldur:

rust
fn main() {
    let sayilar: Vec<i32> = "1,2,3".split(',').map(|s| s.parse().unwrap()).collect();
    println!("{sayilar:?}");

    let sonuc: Result<Vec<i32>, _> = "1,x,3".split(',').map(|s| s.parse::<i32>()).collect();
    println!("{sonuc:?}");
}
// çıktı: [1, 2, 3]
// çıktı: Err(ParseIntError { kind: InvalidDigit })

? operatörünü main içinde kullanmak

? operatörü hata durumunda değeri çağırana döndürür; bu yüzden yalnızca Result, Option veya uyumlu başka bir tip döndüren fonksiyonlarda kullanılabilir. Dönüş tipi () olan sıradan bir main içinde kullanmak E0277 verir:

rust
fn main() {
    let n: i32 = "42".parse()?;
    println!("{n}");
}
// error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)

Kitabın hata yönetimi bölümünde gösterildiği gibi main fonksiyonu Result döndürebilir. Aşağıda ilk dönüşüm başarılı olur, ikincisi başarısız olur ve main hatayı döndürür. Program standart hata çıktısına hatanın Debug biçimini yazar ve sıfırdan farklı bir kodla, denememizde 1 ile çıkar:

rust
use std::num::ParseIntError;

fn main() -> Result<(), ParseIntError> {
    let n: i32 = "42".parse()?;
    println!("{n}");
    let m: i32 = "kırk".parse()?;
    println!("{m}");
    Ok(())
}
// çıktı: 42
// çıktı: Error: ParseIntError { kind: InvalidDigit }

Farklı hata tiplerini tek bir main içinde ? ile kullanmak için Box<dyn Error> veya kendi hata tipiniz gerekir; bu konu Result ve hata yönetimi ile özel hata tipi ve Error trait yazılarında.

E0308: mismatched types

E0308, bir ifadenin derleyicinin beklediğinden farklı bir tipte olduğunu söyler. Mesajın kalbi her zaman expected X, found Y kalıbıdır ve bir etiket de beklentinin nereden doğduğunu gösterir (expected due to this). Hata kodu dizinine göre en yaygın durum, fonksiyon parametresiyle uyuşmayan bir argüman vermektir; aşağıda bunun dışında da çok sık karşılaşılan dört durumu inceliyoruz.

Fonksiyon sonundaki noktalı virgül

Rust'ta fonksiyonun son ifadesi dönüş değeridir. Sonuna noktalı virgül koyduğunuzda ifade deyime dönüşür ve değer üretmez; fonksiyon bu durumda () döndürür:

rust
fn kare(x: i32) -> i32 {
    x * x;
}

fn main() {
    println!("{}", kare(4));
}
// error[E0308]: mismatched types

Rust 1.99.1 bu hatayı çok net açıklar: dönüş tipinde expected `i32`, found `()` yazar, fonksiyon adının altına gövdede kuyruk ifadesi veya return olmadığı için örtük olarak () döndürdüğünü not eder ve help: remove this semicolon to return this value der. Noktalı virgülü kaldırmak yeterlidir. İfade ile deyim farkı fonksiyonlar, ifade ve deyim yazısında ayrıntılı.

if ve else dallarının farklı tip döndürmesi

if bir ifade olduğu için her iki dalı aynı tipte değer üretmelidir. Mesaj bu kez `if` and `else` have incompatible types diye başlar ve ilk dalı beklentinin kaynağı olarak gösterir:

rust
fn main() {
    let stok = 0;
    let durum = if stok > 0 { "var" } else { 0 };
    println!("{durum}");
}
// error[E0308]: `if` and `else` have incompatible types

Dallar aynı tipe getirilir. Burada ikisi de &str olunca kod derlendi ve yok yazdı. Bazen gerçekten iki farklı tür sonuç vardır; o zaman bir enum veya Option tanımlamak daha doğru bir modeldir. Kontrol akışı ifadeleri için if, loop, while ve for yazısına bakabilirsiniz.

String beklenen yere &amp;str vermek

Bir dizgi sabitinin tipi &str tipidir, String değil. Parametresi String olan bir fonksiyona sabit vermek E0308 verir ve derleyici .to_string() ile dönüştürmeyi önerir:

rust
fn selamla(ad: String) {
    println!("Merhaba {ad}");
}

fn main() {
    selamla("Zeynep");
}
// error[E0308]: mismatched types

Öneri işe yarar, ama fonksiyon metni yalnızca okuyorsa daha iyi çözüm parametreyi &str yapmaktır. Böylece hem sabitleri hem de &String değerlerini kabul eder; &String otomatik olarak &str tipine dönüşür. İki tip arasındaki fark String ve &str farkı yazısında:

rust
fn selamla(ad: &str) {
    println!("Merhaba {ad}");
}

fn main() {
    selamla("Zeynep");
    let sahipli = String::from("Kaan");
    selamla(&sahipli);
}
// çıktı: Merhaba Zeynep
// çıktı: Merhaba Kaan

Option&lt;T&gt; değerini doğrudan T gibi kullanmak

Option<i32> bir i32 değildir; değer olmayabilir ve Rust bunu sizin ele almanızı ister:

rust
fn main() {
    let x: Option<i32> = Some(3);
    let y: i32 = x;
    println!("{y}");
}
// error[E0308]: mismatched types

Mesaj expected `i32`, found `Option<i32>` der ve Option::expect çağırmayı önerir; ancak bu, değer None olduğunda panik demektir. Varsayılan bir değer kabul edilebilirse unwrap_or, iki durumu ayrı ele almak gerekiyorsa match daha güvenlidir. Option metotları için enum ve Option yazısına bakabilirsiniz:

rust
fn main() {
    let x: Option<i32> = Some(3);
    let y: i32 = x.unwrap_or(0);
    let z: i32 = match x {
        Some(n) => n * 2,
        None => 0,
    };
    println!("{y} {z}");
}
// çıktı: 3 6

E0425: cannot find value in this scope

E0425, kullandığınız bir adın o kapsamda çözülemediğini söyler. Hata kodu dizini nedenleri sayar: adın yanlış yazılması, tanımın başka bir kapsamda kalması veya başka bir modüldeki öğenin use ile içe aktarılmamış ya da pub yapılmamış olması. Yazım hatalarında Rust 1.99.1 benzer adlı bir yerel değişkeni kendisi önerir:

rust
fn main() {
    let toplam = 10;
    println!("{}", topla);
}
// error[E0425]: cannot find value `topla` in this scope

Bu örnekte yardım satırı a local variable with a similar name exists diyerek toplam adını önerir. Değişken bir blok içinde tanımlandıysa ve blok dışında kullanılıyorsa, öneri gelmeyebilir; o durumda tanımın kapsamını kontrol edin. Başka bir modüldeki fonksiyonlar için yol ve görünürlük kuralları modül sistemi: mod, use, pub yazısında.

E0433: undeclared type or module

E0433, bir yolun başındaki tip, modül veya crate adının bulunamadığını bildirir. En sık örnek, HashMap tipini içe aktarmadan kullanmaktır; çünkü Vec ve String prelude içindeyken HashMap değildir. Hata kodu dizinindeki mesaj metni eski bir derleyiciye aittir; Rust 1.99.1 aynı kod için şunu yazdı:

rust
fn main() {
    let mut sayac = HashMap::new();
    sayac.insert("a", 1);
    println!("{:?}", sayac.get("a"));
}
// error[E0433]: cannot find type `HashMap` in this scope

Yardım satırı doğrudan eklenecek satırı verir: use std::collections::HashMap;. Bu satırı dosyanın başına eklediğinizde kod derlenir. Hata kodu dizini iki durumu daha hatırlatır: harici bir crate kullanıyorsanız Cargo.toml dosyasına bağımlılık olarak eklenmiş olmalıdır; kendi crate'inizdeki bir modülü kullanıyorsanız yolun başına crate:: yazmanız gerekebilir. Bağımlılık eklemek için Cargo.toml ve bağımlılıklar yazısına bakabilirsiniz.

Derleyici mesajını okumanın kısa yolu

Derleyici mesajını okumanın kısa yolu: E0277, E0308, E0425, E0433

Bu dört hatada mesajlar benzer bir düzen izler ve okuma alışkanlığı kazanmak çözüm süresini kısaltır. İlk satır hata kodunu ve özeti verir. Ardından gelen --> satırı dosya, satır ve sütunu gösterir. Altı çizili ^^^ işaretleri sorunlu ifadeyi, tire işaretli ikincil etiketler ise beklentinin kaynağını gösterir. note: satırları bağlam verir (örneğin bir sınırın hangi fonksiyonda konduğunu), help: satırları ise somut bir düzeltme önerir. Son satırdaki rustc --explain E0277 komutu, hata kodu dizinindeki açıklamanın tamamını terminalde gösterir.

KodMesajın özüİlk bakılacak yerTipik çözüm
E0277trait bound is not satisfied, doesn't implementHangi tip, hangi traitimpl yazmak, sınır eklemek, tip dönüştürmek
E0308mismatched types, expected X found Yexpected due to this etiketiNoktalı virgül, dal tipleri, dönüşüm metodu
E0425cannot find value in this scopeYazım ve kapsamAdı düzeltmek, use veya pub eklemek
E0433cannot find type, use of undeclared typeuse satırları ve Cargo.tomluse std::collections::HashMap gibi içe aktarma

Sık yapılan hatalar

  • Generic fonksiyonda sınırı gövdeye bakarak beklemek: Rust yalnızca imzaya bakar; gövdede kullandığınız her yetenek imzada sınır olarak yazmalıdır.
  • Sayı tiplerini as ile gelişigüzel çevirmek: Kayıpsız dönüşümler için From, başarısız olabilecekler için TryFrom kullanın; as sessizce kırpabilir.
  • Option ve Result değerlerini unwrap ile susturmak: Derleyicinin önerdiği expect panik riskini taşır; match, unwrap_or veya ? çoğu zaman daha doğrudur.
  • Display ile Debug'ı karıştırmak: {} için Display, {:?} için Debug gerekir; Display türetilemez.
  • Fonksiyon sonuna alışkanlıkla noktalı virgül koymak: Değer döndürmesi gereken son satırda noktalı virgül, dönüşü () yapar.

Sık Sorulan Sorular

E0277 bir tip hatası mı, trait hatası mı?

Trait hatasıdır, ama sonuç olarak bir tipin beklenen yeteneği taşımadığını söyler. Operatörler de trait'ler üzerinden çalıştığı için i32 * f64 gibi işlemler E0308 değil E0277 verir.

Derleyici neden i32 değerini otomatik olarak f64'e çevirmiyor?

Rust sayı tipleri arasında örtük dönüşüm yapmaz; dönüşümü açıkça yazmanızı ister. Bu sayede kayıp olabilecek dönüşümler kodda görünür kalır.

Hata kodu dizinindeki mesaj neden derleyicimin mesajından farklı?

Dizindeki örnek yorumlar bazı hata kodlarında eski derleyici sürümlerinin metnini yansıtır. Örneğin E0433 için dizin "failed to resolve" yazarken Rust 1.99.1 "cannot find type `HashMap` in this scope" yazdı. Hata kodu aynıdır; anlam değişmez.

? operatörünü Option döndüren fonksiyonda kullanabilir miyim?

Evet. Mesajın da belirttiği gibi ?, Result veya Option döndüren fonksiyonlarda kullanılabilir. Ancak aynı fonksiyonda Result ve Option değerlerini karıştırırsanız birini diğerine dönüştürmeniz gerekir.

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 · Trait tanımlama · From, Into ve TryFrom · Result ve hata yönetimi · Display ve Debug · E0382 move hataları