Rust'ta Ownership ve Borrowing: Eksiksiz Kılavuz
Rust'ın ownership ve borrowing sistemini ustalaşın. Sahiplik kuralları, referanslar, lifetime'lar ve gelişmiş bellek yönetimi desenleri.

Ownership sistemi, Rust'ı diğer tüm programlama dillerinden ayıran özelliktir. Bu benzersiz yaklaşım, çöp toplayıcısı olmadan bellek güvenliğini garanti eder ve hataları çalışma zamanı yerine derleme zamanında yakalar. Bu kapsamlı kılavuz, ownership ve borrowing mekanizmalarını temellerden gelişmiş üretim desenlerine kadar inceler.
Rust derleyicisi titiz bir programlama asistanı gibi davranır: derleme zamanında engellenen her ownership hatası, üretimde önlenen potansiyel bir bug demektir.
Ownership'ın Üç Temel Kuralı
Ownership sistemi üç basit ama katı kurala dayanır. Bu kurallar içselleştirildiğinde, Rust'ın zihinsel modeli doğal ve öngörülebilir hale gelir.
// Demonstration of the three fundamental rules
fn main() {
// Rule 1: Each value has exactly ONE owner
let s1 = String::from("hello"); // s1 is the sole owner
// Rule 2: There can only be one owner at a time
let s2 = s1; // Ownership transferred (moved) from s1 to s2
// println!("{}", s1); // Compile ERROR: s1 no longer exists
println!("s2 = {}", s2); // Only s2 is valid now
// Rule 3: When the owner goes out of scope, the value is dropped
{
let s3 = String::from("temporary");
println!("s3 inside block = {}", s3);
} // s3 is automatically freed here (drop is called)
// println!("{}", s3); // ERROR: s3 no longer exists
}Bu üç kural tüm bug kategorilerini ortadan kaldırır: use-after-free, double-free ve bellek sızıntıları. Derleyici, bu kurallara uyulduğunu statik olarak doğrular.
Move vs Copy: Aktarım Semantiğini Anlamak
Atama davranışı veri tipine bağlıdır. Copy trait'ini uygulayan tipler kopyalanırken, diğerleri move ile aktarılır.
// Distinction between Copy types and Move types
fn main() {
// Copy types: values stored on the stack, known size
let x: i32 = 42;
let y = x; // x is COPIED, not moved
println!("x = {}, y = {}", x, y); // Both are valid
// Other Copy types: f64, bool, char, tuples of Copy types
let point = (3.0, 4.0);
let point_copy = point; // Tuple copy
println!("Original: {:?}, Copy: {:?}", point, point_copy);
// Move types: values on the heap, dynamic size
let s1 = String::from("owned");
let s2 = s1; // s1 is MOVED to s2
// println!("{}", s1); // ERROR: value moved
println!("s2 = {}", s2);
// Vec, HashMap, Box are also Move types
let vec1 = vec![1, 2, 3];
let vec2 = vec1; // Move, not copy
// println!("{:?}", vec1); // ERROR
println!("vec2 = {:?}", vec2);
}
// Explicit clone to duplicate Move types
fn explicit_clone() {
let original = String::from("important data");
let clone = original.clone(); // Explicit duplication (memory cost)
println!("Original: {}", original); // Still valid
println!("Clone: {}", clone); // Independent copy
}Move/Copy ayrımı temeldir: atamanın sahipliği aktarmasını mı yoksa bağımsız bir kopya oluşturmasını mı belirler.
.clone() çağrısı bilinçli olmalıdır. Klonlarla dolu bir kod tasarım sorununa işaret edebilir. Borrowing genellikle daha iyi bir çözümdür.
Borrowing: Değişmez ve Değişebilir Referanslar
Borrowing, sahipliği almadan bir değere erişim sağlar. Bu mekanizma Rust kodunu hem güvenli hem de performanslı kılar.
// Immutable and mutable references
fn main() {
let s = String::from("hello");
// Immutable reference: read-only, multiple allowed
let len = calculate_length(&s); // Immutable borrow
println!("'{}' has {} characters", s, len); // s still valid
// Multiple simultaneous immutable references: OK
let r1 = &s;
let r2 = &s;
let r3 = &s;
println!("r1={}, r2={}, r3={}", r1, r2, r3);
}
fn calculate_length(s: &String) -> usize {
// s is a reference, not the owner
s.len()
} // s goes out of scope but doesn't drop anything (not owner)
// Mutable references: modification allowed
fn mutable_borrowing() {
let mut s = String::from("hello");
change(&mut s); // Mutable borrow
println!("After modification: {}", s);
}
fn change(s: &mut String) {
s.push_str(", world!"); // Modification via mutable reference
}Borrowing'in altın kuralı: ya birden fazla değişmez referans VEYA tek bir değişebilir referans, asla ikisi aynı anda olmaz.
Borrow Checker Kuralları
Borrow checker, borrowing kurallarını doğrulayan derleyici bileşenidir. Hatalarını anlamak, sorunları hızlıca çözmeyi sağlar.
// Strict borrow checker rules
fn main() {
// RULE 1: No mutable reference with immutable references
let mut s = String::from("hello");
let r1 = &s; // Immutable reference: OK
let r2 = &s; // Another immutable reference: OK
// let r3 = &mut s; // ERROR: cannot borrow as mutable
println!("{} and {}", r1, r2);
// AFTER using r1 and r2, they are "dead"
let r3 = &mut s; // Now OK: r1 and r2 no longer used
r3.push_str(" world");
println!("{}", r3);
// RULE 2: Only one mutable reference at a time
let mut data = String::from("exclusive");
let ref1 = &mut data;
// let ref2 = &mut data; // ERROR: already borrowed mutably
ref1.push_str("!");
println!("{}", ref1);
}
// RULE 3: References cannot outlive the data
fn dangling_reference_prevented() {
let reference;
{
let s = String::from("short-lived");
// reference = &s; // ERROR: s doesn't live long enough
}
// s is dropped here, reference would be invalid
// Solution: move the value out of the scope
let owned_outside;
{
let s = String::from("moved out");
owned_outside = s; // Move, not reference
}
println!("{}", owned_outside); // OK: owned_outside is the owner
}Borrow checker Non-Lexical Lifetimes (NLL) kullanır: bir referans yalnızca son kullanımına kadar aktif sayılır, scope sonuna kadar değil.
Rust mülakatlarında başarılı olmaya hazır mısın?
İnteraktif simülatörler, flashcards ve teknik testlerle pratik yap.
Lifetime'lar: Referansların Süresini Belirtmek
Lifetime'lar, referansların geçerli kalmasını derleyicinin doğrulamasına yardımcı olan açıklamalardır. Çoğu zaman otomatik olarak çıkarılır.
// Explicit lifetime annotations
// Without annotation: compiler infers lifetimes
fn first_word(s: &str) -> &str {
match s.find(' ') {
Some(i) => &s[..i],
None => s,
}
}
// With explicit annotation: same function
fn first_word_explicit<'a>(s: &'a str) -> &'a str {
// 'a means: returned reference lives as long as the input
match s.find(' ') {
Some(i) => &s[..i],
None => s,
}
}
// When annotations are necessary: multiple references
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
// Compiler cannot guess which reference is returned
if x.len() > y.len() {
x
} else {
y
}
}
fn main() {
let string1 = String::from("long string");
let result;
{
let string2 = String::from("xyz");
result = longest(&string1, &string2);
println!("Longest: {}", result); // OK here
}
// println!("{}", result); // ERROR if uncommented: string2 dropped
}Lifetime'lar verinin ne kadar yaşadığını değiştirmez; farklı referansların ömürleri arasındaki ilişkileri tanımlar.
Struct'larda Lifetime
Bir struct referans içerdiğinde, struct'ın referans verilen veriden daha uzun yaşamamasını garanti etmek için lifetime'lar belirtilmelidir.
// Structs containing references
// Struct with reference: lifetime required
struct ImportantExcerpt<'a> {
part: &'a str, // This reference must live at least as long as the struct
}
impl<'a> ImportantExcerpt<'a> {
// Method returning a reference with the same lifetime
fn level(&self) -> i32 {
3
}
// Elision rule: &self implies the output lifetime
fn announce_and_return_part(&self, announcement: &str) -> &str {
println!("Attention: {}", announcement);
self.part // Returns with 'a lifetime from self
}
}
fn main() {
let novel = String::from("Call me Ishmael. Some years ago...");
let first_sentence = novel.split('.').next().unwrap();
let excerpt = ImportantExcerpt {
part: first_sentence, // OK: novel outlives excerpt
};
println!("Excerpt: {}", excerpt.part);
println!("Level: {}", excerpt.level());
}
// Static lifetime: reference valid for the entire program duration
fn static_lifetime_example() {
let s: &'static str = "This string is in the binary";
// String literals always have 'static lifetime
println!("{}", s);
}Lifetime elision kuralları sıklıkla yaygın durumlarda açıklamaları atlamayı sağlar ve kodu daha okunabilir kılar.
Gelişmiş Desenler: Interior Mutability
Bazen değişebilirlik derleme zamanı yerine çalışma zamanında doğrulanmalıdır. Rust bu desen için tipler sağlar: RefCell ve Cell.
// Interior mutability with RefCell and Cell
use std::cell::{Cell, RefCell};
// Cell: for Copy types, replaces the entire value
struct Counter {
count: Cell<u32>, // Mutable despite &self
}
impl Counter {
fn new() -> Counter {
Counter { count: Cell::new(0) }
}
fn increment(&self) {
// Modification via immutable reference!
self.count.set(self.count.get() + 1);
}
fn get(&self) -> u32 {
self.count.get()
}
}
// RefCell: for non-Copy types, checks at runtime
struct CachedValue {
value: RefCell<Option<String>>,
}
impl CachedValue {
fn new() -> CachedValue {
CachedValue { value: RefCell::new(None) }
}
fn get_or_compute(&self, compute: impl FnOnce() -> String) -> String {
// borrow() for reading, borrow_mut() for writing
if self.value.borrow().is_none() {
*self.value.borrow_mut() = Some(compute());
}
self.value.borrow().as_ref().unwrap().clone()
}
}
fn main() {
let counter = Counter::new();
counter.increment();
counter.increment();
println!("Counter: {}", counter.get()); // 2
let cache = CachedValue::new();
let result = cache.get_or_compute(|| {
println!("Expensive computation...");
String::from("result")
});
println!("Value: {}", result);
// Second call: no recomputation
let result2 = cache.get_or_compute(|| String::from("never executed"));
println!("Cache hit: {}", result2);
}RefCell ve Cell, borrowing doğrulamasını çalışma zamanına taşır. Bir kural ihlali derleme hatası yerine panic'e yol açar.
RefCell::borrow_mut() değer zaten ödünç alınmışsa panic yapar. Açık hata yönetimi için try_borrow_mut() kullanılmalıdır.
Smart Pointer'lar ve Ownership
Box, Rc ve Arc gibi smart pointer'lar belirli kullanım durumları için farklı ownership stratejileri sunar.
// Box, Rc, and Arc for different ownership patterns
use std::rc::Rc;
use std::sync::Arc;
use std::thread;
// Box: single owner, data on the heap
fn box_example() {
let boxed = Box::new(vec![1, 2, 3, 4, 5]);
println!("Boxed vec: {:?}", boxed);
// Useful for: recursive types, large objects, trait objects
}
// Rc: reference counting, multiple owners (single-thread)
fn rc_example() {
let data = Rc::new(String::from("shared data"));
let clone1 = Rc::clone(&data); // Increments the counter
let clone2 = Rc::clone(&data);
println!("Count: {}", Rc::strong_count(&data)); // 3
println!("All share: {}, {}, {}", data, clone1, clone2);
} // Freed when counter reaches 0
// Arc: thread-safe Rc (Atomic Reference Counting)
fn arc_example() {
let data = Arc::new(vec![1, 2, 3]);
let handles: Vec<_> = (0..3).map(|i| {
let data_clone = Arc::clone(&data);
thread::spawn(move || {
println!("Thread {}: {:?}", i, data_clone);
})
}).collect();
for handle in handles {
handle.join().unwrap();
}
}
fn main() {
box_example();
rc_example();
arc_example();
}Smart pointer seçimi ownership desenine bağlıdır: tekil (Box), tek thread'de paylaşımlı (Rc) veya çok thread'de paylaşımlı (Arc).
Pratik Ownership Desenleri
İşte kodu ownership sistemi etrafında yapılandırmak için yaygın desenler.
// Practical patterns for ownership management
// Pattern 1: Builder pattern with chained ownership
struct RequestBuilder {
url: String,
headers: Vec<(String, String)>,
timeout: Option<u64>,
}
impl RequestBuilder {
fn new(url: &str) -> Self {
RequestBuilder {
url: url.to_string(),
headers: Vec::new(),
timeout: None,
}
}
// Consumes self and returns the new self
fn header(mut self, key: &str, value: &str) -> Self {
self.headers.push((key.to_string(), value.to_string()));
self // Returns ownership
}
fn timeout(mut self, seconds: u64) -> Self {
self.timeout = Some(seconds);
self
}
fn build(self) -> Request {
Request {
url: self.url,
headers: self.headers,
timeout: self.timeout.unwrap_or(30),
}
}
}
struct Request {
url: String,
headers: Vec<(String, String)>,
timeout: u64,
}
// Pattern 2: Cow (Copy-on-Write) to avoid allocations
use std::borrow::Cow;
fn process_text(input: &str) -> Cow<str> {
if input.contains("REPLACE") {
// Allocation only if modification needed
Cow::Owned(input.replace("REPLACE", "NEW"))
} else {
// No allocation, returns a reference
Cow::Borrowed(input)
}
}
// Pattern 3: Take to extract from an Option
fn extract_value(data: &mut Option<String>) -> String {
data.take().unwrap_or_else(|| String::from("default"))
// take() replaces with None and returns ownership of the value
}
fn main() {
// Builder pattern
let request = RequestBuilder::new("https://api.example.com")
.header("Authorization", "Bearer token")
.header("Content-Type", "application/json")
.timeout(60)
.build();
println!("URL: {}, Timeout: {}s", request.url, request.timeout);
// Cow pattern
let text1 = process_text("hello world"); // No allocation
let text2 = process_text("hello REPLACE"); // Allocation
println!("{} | {}", text1, text2);
// Take pattern
let mut optional = Some(String::from("extracted"));
let value = extract_value(&mut optional);
println!("Value: {}, Option: {:?}", value, optional); // None
}Bu desenler, ergonomik ve performanslı API'ler oluşturmak için ownership sisteminden yararlanır.
Pratik yapmaya başla!
Mülakat simülatörleri ve teknik testlerle bilgini test et.
Sonuç
Rust'ın ownership ve borrowing sistemi, bellek yönetiminde bir paradigma değişimini temsil eder. Bir kez ustalaşıldığında, hem performanslı hem de güvenli kod yazmak için güçlü bir müttefik haline gelir.
Akılda Tutulması Gerekenler:
✅ Üç ownership kuralı: tek sahip, sahipliğin aktarımı, otomatik drop
✅ Borrowing: birden fazla değişmez referans VEYA tek bir özel değişebilir referans
✅ Lifetime'lar: referans ömürleri arasındaki ilişkileri belirtmek
✅ Interior mutability: çalışma zamanında doğrulanan değişebilirlik için RefCell ve Cell
✅ Smart pointer'lar: Box (tekil), Rc (paylaşımlı), Arc (thread-safe)
✅ Pratik desenler: idiyomatik API'ler için Builder, Cow, Take
Borrow checker başlangıçta katı görünebilir, ancak bildirdiği her hata önlenmiş potansiyel bir bug demektir. Pratikle, ownership açısından düşünmek doğal hale gelir ve tüm dillerde kod kalitesini artırır.
Pratik yapmaya başla!
Mülakat simülatörleri ve teknik testlerle bilgini test et.
Etiketler
Paylaş
İlgili makaleler

Rust Ownership ve Borrowing: Her Seyi Aciklayan Kapsamli Rehber
Rust ownership ve borrowing kavramlarini pratik orneklerle ogrenin. Move semantikleri, referanslar, lifetime'lar ve borrow checker ile guvenli, performansli Rust kodu yazmayi kesfedin.

Rust Mulakat Sorulari: 2026 Kapsamli Rehber
En sik sorulan 25 Rust mulakat sorusu. Ownership, borrowing, lifetime, trait, async/await, es zamanlilik konulari detayli cevaplar ve kod ornekleriyle.

Rust: 2026'da Deneyimli Geliştiriciler İçin Temel Kavramlar
Mevcut programlama bilgisiyle Rust'ı hızlıca öğrenmek mümkün. Sahiplik, ödünç alma, yaşam süreleri ve temel kalıplar; C++, Java veya Python'dan gelen geliştiriciler için açıklandı.