Rust'ta Vec<T>, aynı tipteki değerleri bellekte art arda tutan ve gerektiğinde büyüyebilen bir dizidir. Boş bir vektörü Vec::new() ile, başlangıç değerli bir vektörü vec![1, 2, 3] makrosuyla oluşturur, push ile sona eleman ekler, v[i] ya da v.get(i) ile okur ve for x in &v ile tek tek gezersiniz. Bu yazı bu temel işlemleri, aralarındaki farkları ve en sık karşılaşılan derleyici hatalarını çalışan örneklerle anlatıyor.
Örneklerin hepsi Rust 1.99.1 ve edition 2024 ile derlenip çalıştırıldı; kod içindeki // çıktı: yorumları programın gerçekten yazdığı satırlardır. Rust'ın genel tanıtımı için Rust nedir yazısına, serinin tamamı için Rust öğrenme rehberi sayfasına bakabilirsiniz. Burada yalnızca vektörün kendisine odaklanıyoruz.
Vec nedir, bellekte nasıl durur?

Standart kütüphane belgesine göre bir Vec her zaman üç parçadan oluşur: yığındaki (heap) bellek bloğunu gösteren bir işaretçi, o blokta kaç eleman için yer ayrıldığını söyleyen kapasite ve şu anda gerçekten kaç eleman bulunduğunu söyleyen uzunluk. Vektör değişkeninin kendisi (bu üçlü) yığıtta (stack) durur, elemanlar ise yığındaki bitişik bir bloktadır. Elemanların bitişik olması, indeksle erişimi sabit zamanlı yapar ve vektörü bir dilime (&[T]) çevirmeyi neredeyse bedavaya getirir.
Uzunluk kapasiteyi aştığında vektör daha büyük bir blok ayırır, eski elemanları oraya taşır ve eski bloğu bırakır. Belge büyüme stratejisi için belirli bir oran garanti etmez, yalnızca push işleminin ortalamada (amortize) sabit zamanlı olacağını garanti eder. Bir başka önemli kural: vektör kendiliğinden asla küçülmez. Elemanları silseniz de ayrılmış bellek yerinde kalır; geri vermek isterseniz shrink_to_fit ya da shrink_to çağırmanız gerekir.
Vektör oluşturmanın yolları
En sık kullanılan dört yol aşağıdadır. Vec::new() boş bir vektör üretir ve belgeye göre ilk eleman eklenene kadar bellek ayırmaz. İçine hiçbir şey koymadığınız için derleyici eleman tipini çıkaramaz; bu yüzden Vec<i32> gibi bir tip notu gerekir. vec! makrosu verdiğiniz değerlerden tipi kendisi çıkarır; tam sayı değerleri için varsayılan tip i32'dir. vec![0; 5] biçimi aynı değeri beş kez koyar.
fn main() {
let bos: Vec<i32> = Vec::new();
let sayilar = vec![1, 2, 3];
let sifirlar = vec![0; 5];
let diziden = Vec::from([10, 20, 30]);
println!("{bos:?} {sayilar:?} {sifirlar:?} {diziden:?}");
let mut liste = Vec::with_capacity(10);
println!("uzunluk: {}, kapasite en az 10: {}", liste.len(), liste.capacity() >= 10);
for i in 0..3 {
liste.push(i * 100);
}
println!("{liste:?}, uzunluk: {}", liste.len());
}
// çıktı: [] [1, 2, 3] [0, 0, 0, 0, 0] [10, 20, 30]
// çıktı: uzunluk: 0, kapasite en az 10: true
// çıktı: [0, 100, 200], uzunluk: 3Vec::with_capacity(10) uzunluğu sıfır olan ama en az on eleman için yer ayırmış bir vektör verir. Burada sık yapılan bir yanlış anlama var: kapasite ayırmak eleman eklemek demek değildir, yani liste[0] hâlâ geçersizdir. Belge ayrıca kapasitenin "en az" istenen değer olduğunu söyler; tam değeri öğrenmek isterseniz capacity() çağırın. Kaç eleman geleceğini önceden biliyorsanız with_capacity kullanmak gereksiz yeniden ayırmaları önler. Aynı değerle doldurulmuş bir vektör için belge vec![0; n] biçiminin ayırma ve doldurmayı ayrı ayrı yapmaktan daha verimli olabileceğini de belirtir.
Vektörü sonradan değiştirecekseniz değişkeni let mut ile tanımlamanız gerekir; Rust'ta değişkenler varsayılan olarak değiştirilemez. Bu kuralın ayrıntısı Rust değişkenleri, mut ve shadowing yazısında anlatılıyor.
Elemanlara erişim: [] ile get() farkı
Bir elemanı okumanın iki yolu vardır ve aralarındaki fark, indeks geçersiz olduğunda programın ne yapacağıdır. &v[2] doğrudan elemana bir referans verir. v.get(2) ise Option<&T> döndürür: eleman varsa Some(&eleman), yoksa None. first() ve last() da aynı mantıkla Option döndürür, boş vektörde paniğe yol açmaz.
fn main() {
let notlar = vec![85, 92, 78, 64, 99];
let ucuncu: &i32 = ¬lar[2];
println!("Üçüncü not: {ucuncu}");
match notlar.get(2) {
Some(n) => println!("get(2): {n}"),
None => println!("Böyle bir eleman yok"),
}
println!("get(100): {:?}", notlar.get(100));
println!("ilk: {:?}, son: {:?}", notlar.first(), notlar.last());
let bos: Vec<i32> = Vec::new();
println!("boş vektörde first: {:?}", bos.first());
}
// çıktı: Üçüncü not: 78
// çıktı: get(2): 78
// çıktı: get(100): None
// çıktı: ilk: Some(85), son: Some(99)
// çıktı: boş vektörde first: NoneAynı vektörde 100 numaralı indekse köşeli parantezle erişmeye çalışırsanız program çalışma anında panikler ve durur. Panik mesajı vektörün uzunluğunu ve istenen indeksi açıkça yazar:
fn main() {
let v = vec![1, 2, 3, 4, 5];
let olmayan = v[100];
println!("{olmayan}");
}
// panik: index out of bounds: the len is 5 but the index is 100Hangisini seçeceğiniz, geçersiz bir indeksin programınızda "olmaması gereken bir hata" mı yoksa "arada bir olabilecek normal bir durum" mu olduğuna bağlıdır. The Rust Book'taki örnek bunu iyi özetler: indeks kullanıcının yazdığı bir sayıdan geliyorsa get ile None durumunu yakalayıp kullanıcıya kaç eleman olduğunu söylemek, programı çökertmekten çok daha iyidir. İndeksin geçerli olduğundan emin olduğunuz iç hesaplamalarda ise [] kodu kısa tutar. Option ile çalışmanın diğer yolları Rust enum ve Option yazısında anlatılıyor.
Referans tutarken push yapmak: E0502
Vektörle çalışırken yeni başlayanların en sık takıldığı hata budur. Aşağıdaki kod ilk elemana değiştirilemez bir referans alır, sonra vektöre eleman ekler ve en sonda o referansı kullanır. Derleyici bunu kabul etmez:
fn main() {
let mut v = vec![1, 2, 3, 4, 5];
let ilk = &v[0];
v.push(6);
println!("İlk eleman: {ilk}");
}
// error[E0502]: cannot borrow `v` as mutable because it is also borrowed as immutableKod ilk bakışta zararsız görünür: sona eklenen eleman neden ilk elemana olan referansı etkilesin? Nedeni vektörün bellek düzenidir. push sırasında kapasite yetmezse vektör yeni bir blok ayırıp elemanları oraya taşır; bu durumda ilk referansı artık serbest bırakılmış belleği gösterirdi. Borrow kuralları bu ihtimali derleme aşamasında engeller. Çözüm, referansı değişiklikten önce kullanıp bitirmek ya da referans yerine değerin kendisini almaktır. i32 gibi Copy tipleri için let ilk = v[0]; değeri kopyalar ve sorun ortadan kalkar:
fn main() {
let mut v = vec![1, 2, 3, 4, 5];
let ilk = v[0]; // i32 Copy olduğu için değer kopyalanır
v.push(6);
println!("İlk eleman: {ilk}, uzunluk: {}", v.len());
}
// çıktı: İlk eleman: 1, uzunluk: 6String gibi Copy olmayan eleman tiplerinde aynı yöntem işe yaramaz; o durumda .clone() ile kopya almanız ya da kodun sırasını değiştirmeniz gerekir. Borrow kurallarının kendisi Rust referans ve borrowing yazısında, bu hata kodunun diğer biçimleri ise E0499 ve E0502 borrow checker hataları yazısında ayrıntılı olarak ele alınıyor.
Vektör üzerinde döngü
Elemanları tek tek işlemek için indeksle uğraşmak yerine doğrudan vektörü gezmek hem daha okunur hem de daha güvenlidir. Üç farklı biçim vardır ve aralarındaki fark sahipliktir: for x in &v her elemana değiştirilemez bir referans verir, for x in &mut v değiştirilebilir bir referans verir, for x in v ise vektörün sahipliğini alır ve onu tüketir.
fn main() {
let mut fiyatlar = vec![100, 32, 57];
for f in &fiyatlar {
print!("{f} ");
}
println!();
for f in &mut fiyatlar {
*f += 50;
}
println!("{fiyatlar:?}");
for (sira, f) in fiyatlar.iter().enumerate() {
println!("{sira}: {f}");
}
let toplam: i32 = fiyatlar.iter().sum();
println!("toplam: {toplam}");
for f in fiyatlar {
print!("{f} ");
}
println!();
// fiyatlar burada artık kullanılamaz: for döngüsü onu tüketti
}
// çıktı: 100 32 57
// çıktı: [150, 82, 107]
// çıktı: 0: 150
// çıktı: 1: 82
// çıktı: 2: 107
// çıktı: toplam: 339
// çıktı: 150 82 107Değiştirilebilir referansla gezerken değerin kendisine ulaşmak için *f biçiminde yıldız (dereference) operatörü kullanılır. Döngü gövdesinde vektöre eleman eklemeye ya da vektörden eleman silmeye çalışırsanız yukarıdaki E0502 hatasının benzerini alırsınız, çünkü döngü vektörün tamamını ödünç almış durumdadır. Silme gibi işlemler için aşağıda anlatılan retain ya da drain gibi metotlar tam bu amaçla vardır. enumerate, sum, map ve filter gibi araçların tamamı Rust iterator: map, filter, collect yazısında anlatılıyor.
Ekleme, silme ve düzenleme metotları

Vec çok sayıda metot sunar. Günlük kodda en çok işinize yarayacak olanları tek örnekte topladık. Her satırın çıktısı, metodun ne yaptığını gösterir:
fn main() {
let mut v = vec!['a', 'b', 'c'];
v.insert(1, 'x');
println!("insert: {v:?}");
let silinen = v.remove(1);
println!("remove: {silinen} {v:?}");
let mut w = vec!["elma", "armut", "kiraz", "erik"];
let cikan = w.swap_remove(0);
println!("swap_remove: {cikan} {w:?}");
let mut sayilar = vec![5, 3, 8, 3, 1, 8, 8];
sayilar.sort();
println!("sort: {sayilar:?}");
sayilar.dedup();
println!("dedup: {sayilar:?}");
sayilar.retain(|&x| x > 2);
println!("retain: {sayilar:?}");
let mut yigin = vec![1, 2, 3];
while let Some(ust) = yigin.pop() {
print!("{ust} ");
}
println!("| pop sonrası: {yigin:?}");
let mut a = vec![1, 2, 3, 4, 5, 6];
let alinan: Vec<i32> = a.drain(1..3).collect();
println!("drain: {alinan:?} kalan: {a:?}");
a.truncate(2);
a.extend([7, 8]);
a.extend_from_slice(&[9]);
println!("truncate + extend: {a:?}");
println!("contains(&8): {}", a.contains(&8));
}
// çıktı: insert: ['a', 'x', 'b', 'c']
// çıktı: remove: x ['a', 'b', 'c']
// çıktı: swap_remove: elma ["erik", "armut", "kiraz"]
// çıktı: sort: [1, 3, 3, 5, 8, 8, 8]
// çıktı: dedup: [1, 3, 5, 8]
// çıktı: retain: [3, 5, 8]
// çıktı: 3 2 1 | pop sonrası: []
// çıktı: drain: [2, 3] kalan: [1, 4, 5, 6]
// çıktı: truncate + extend: [1, 4, 7, 8, 9]
// çıktı: contains(&8): trueBu metotların bazı ayrıntıları hatalardan kaçınmak için önemlidir. insert(i, x) ve remove(i), i konumundan sonraki bütün elemanları kaydırır; belge bunların en kötü durumda vektör uzunluğuyla orantılı süre aldığını söyler. Sıranın önemli olmadığı durumda swap_remove silinen yere son elemanı koyar ve sabit sürede çalışır; örnekte "erik" başa geçti. remove geçersiz indekste, insert ise indeks uzunluktan büyükse panikler; uzunluğa eşit indeksle insert sona eklemekle aynıdır.
dedup yalnızca art arda gelen tekrarları siler; bu yüzden bütün tekrarları temizlemek için önce sıralamanız gerekir, örnekte de önce sort çağırdık. retain koşulu sağlamayan elemanları yerinde siler ve kalanların sırasını korur. pop son elemanı Option olarak döndürür, bu da while let ile vektörü bir yığın (stack) gibi kullanmayı kolaylaştırır. drain verilen aralıktaki elemanları vektörden çıkarıp bir iterator olarak verir. truncate ve clear elemanları atar ama kapasiteye dokunmaz. Sıralamayla ilgili not: sort kararlı (stable) bir sıralamadır ve Vec değil dilim metodudur; vektör dilime otomatik dönüştüğü için doğrudan çağırabilirsiniz.
| Metot | Ne yapar | Dikkat edilecek nokta |
|---|---|---|
| push(x) | Sona ekler | Kapasite doluysa yeniden ayırma yapar |
| pop() | Sondakini çıkarır, Option döndürür | Boş vektörde None verir, paniklemez |
| insert(i, x) | i konumuna ekler | Sonraki elemanları kaydırır; i > len ise panik |
| remove(i) | i konumundakini çıkarır | Sırayı korur ama elemanları kaydırır; geçersiz i panik |
| swap_remove(i) | Yerine son elemanı koyar | Sabit süre ama sıra bozulur |
| retain(f) | Koşula uymayanları siler | Kalanların sırası korunur |
| dedup() | Art arda tekrarları siler | Tüm tekrarlar için önce sort gerekir |
| drain(a..b) | Aralığı çıkarıp iterator verir | Aralık uzunluğu aşarsa panik |
| truncate(n) / clear() | Fazla elemanları atar | Kapasite değişmez |
| get(i) | Option<&T> döndürür | Geçersiz indekste None |
Fonksiyonlara vektör geçirmek: &Vec<T> yerine &[T]
Bir fonksiyon vektörü yalnızca okuyacaksa parametre tipini &Vec<T> değil &[T] yapmak Rust'ta yerleşik bir alışkanlıktır; standart kütüphane belgesi de salt okuma için dilim geçirmenin daha yaygın olduğunu söyler. Dilim parametresi hem vektörün tamamını (&v) hem bir parçasını (&v[..2]) hem de sabit boyutlu dizileri kabul eder. Aşağıdaki örnek ayrıca vektörde farklı tipte değerleri bir enum yardımıyla nasıl tutabileceğinizi gösteriyor:
#[derive(Debug)]
enum Hucre {
Tam(i32),
Ondalik(f64),
Metin(String),
}
fn ortalama(dilim: &[f64]) -> f64 {
if dilim.is_empty() {
return 0.0;
}
dilim.iter().sum::<f64>() / dilim.len() as f64
}
fn main() {
let satir = vec![
Hucre::Tam(3),
Hucre::Metin(String::from("mavi")),
Hucre::Ondalik(10.12),
];
for h in &satir {
match h {
Hucre::Tam(n) => println!("tam sayı: {n}"),
Hucre::Ondalik(x) => println!("ondalık: {x}"),
Hucre::Metin(s) => println!("metin: {s}"),
}
}
let olcumler = vec![2.5, 3.5, 6.0];
println!("ortalama: {}", ortalama(&olcumler));
println!("ilk iki: {}", ortalama(&olcumler[..2]));
}
// çıktı: tam sayı: 3
// çıktı: metin: mavi
// çıktı: ondalık: 10.12
// çıktı: ortalama: 4
// çıktı: ilk iki: 3Vektörün bütün elemanları aynı tipte olmak zorundadır, çünkü derleyicinin her elemanın bellekte ne kadar yer kaplayacağını bilmesi gerekir. Bir tablo satırındaki tam sayı, ondalık sayı ve metin hücreleri gibi farklı türleri tek vektörde tutmanın yolu, hepsini aynı enum'un varyantları yapmaktır. match kullandığınızda derleyici bütün varyantları ele aldığınızı kontrol eder. Hangi tiplerin geleceğini derleme anında bilemiyorsanız enum yetmez; o durumda Rust trait object ve dyn yazısında anlatılan Box<dyn Trait> yaklaşımı kullanılır. Dilim sözdiziminin kendisi için Rust slice ve str dilimleri yazısına bakın.
Vektör kapsam dışına çıkınca
Her sahipli değer gibi vektör de tanımlandığı blok bittiğinde serbest bırakılır ve bu sırada içindeki bütün elemanlar da bırakılır. Vektör String gibi kendi belleğini yöneten değerler tutuyorsa onların belleği de temizlenir; ayrıca bir şey yapmanız gerekmez. Borrow checker, vektörün elemanlarına olan referansların vektörün kendisinden daha uzun yaşamamasını garanti eder. Belgenin küçük ama ilginç bir notu: elemanların hangi sırayla bırakılacağı şu an garanti edilmiyor, geçmişte değişmiş ve yine değişebilir. Elemanlarınızın Drop sırasına bağlı bir davranışınız varsa buna güvenmeyin. Sahiplik ve drop konusunun tamamı Rust ownership kuralları yazısında.
Sık yapılan hatalar
![Sık yapılan hatalar: Kapasiteyi uzunluk sanmak, Kullanıcı girdisini [] ile kullanmak, Döngü içinde vektörü değiştirmek, dedup'un tüm tekrarları sileceğini sanmak](/assets/blog/rust-vec-vektor-kullanimi-b9.webp)
- Kapasiteyi uzunluk sanmak:
Vec::with_capacity(5)sonrasındav[0] = 1yazmak paniğe yol açar, çünkü uzunluk hâlâ sıfırdır. Öncepushile eleman eklemeniz ya davec![0; 5]kullanmanız gerekir. - Kullanıcı girdisini [] ile kullanmak: Dışarıdan gelen indeksler için
gettercih edin; panik yerine kontrol edilebilir birNonealırsınız. - Döngü içinde vektörü değiştirmek:
for x in &viçindev.pushderlenmez. Silmek içinretain, toplu çıkarma içindrain, dönüştürüp yeni vektör üretmek için iterator vecollectkullanın. dedup'un tüm tekrarları sileceğini sanmak: Yalnızca yan yana tekrarları siler; sırasız veride öncesortçağırın ya da HashSet ile sırayı koruyan tekilleştirme yapın.- Baştan çok sık silmek:
remove(0)her seferinde bütün elemanları kaydırır. Belge baştan çıkarma ağırlıklı işler içinVecDeque::pop_frontönerir. - Silinen verinin bellekten temizlendiğini varsaymak: Belge, vektörün silinen veriyi özellikle silmediğini, güvenlik amacıyla buna güvenilmemesi gerektiğini açıkça yazar.
Sık Sorulan Sorular
Vec ile sabit boyutlu dizi [T; N] arasındaki fark nedir?
Dizinin boyu derleme anında sabittir ve genellikle yığıtta durur; vektörün boyu çalışma anında değişebilir ve elemanları yığında tutulur. Eleman sayısı baştan belli ve hiç değişmiyorsa dizi yeterlidir, değişiyorsa vektör kullanın. Dizi tipleri Rust veri tipleri yazısında anlatılıyor.
Vec ne zaman VecDeque ile değiştirilmeli?
Elemanları hem baştan hem sondan ekleyip çıkarıyorsanız, yani bir kuyruk gerekiyorsa VecDeque daha uygundur. Yalnızca sona ekleyip sondan çıkarıyorsanız Vec yeterlidir. Diğer koleksiyonların karşılaştırması BTreeMap, HashSet ve VecDeque yazısında.
Ayrılmış fazla belleği nasıl geri veririm?
shrink_to_fit() kapasiteyi uzunluğa yaklaştırmaya çalışır, shrink_to(n) ise en az n eleman kadar yer bırakarak küçültür. Vektör bunu kendiliğinden yapmaz; boşaltıp yeniden doldurduğunuz bir vektörün yeniden bellek ayırmaması bilinçli bir tasarım tercihidir.
Vektör içinde metin tutarken String mi &str mi kullanmalıyım?
Vektör metinlerin sahibi olacaksa ve metinler çalışma anında üretiliyorsa Vec<String> kullanın. Metinler program boyunca yaşayan sabitlerse ya da başka bir değişkenden ödünç alınıyorsa Vec<&str> yeterlidir. İkisinin farkı Rust String ve &str farkı yazısında anlatılıyor.
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 · String ve &str · HashMap kullanımı · Diğer koleksiyonlar · Iterator · Borrow checker hataları