yoklateknik mülakat

Backend Rs Memory Smart Pointers Unsafe Mülakat Soruları

75 doğrulanmış Backend Rs Memory Smart Pointers Unsafe mülakat sorusu — cevaplarıyla çöz, açıklamalarıyla öğren, gerçek simülasyonda kendini test et.

Gerçek simülasyonu dene →

Örnek sorular

Rs Memory Smart Pointers UnsafeZorluk 1
enum List {
    Cons(i32, List),
    Nil,
}
fn main() {
    let _l = List::Nil;
}

rustc (edition 2021) bu programa ne yapar?
  • aDerlenir; List::Nil 0 byte kapladığı için boş liste heap ayırmadan oluşturulur
  • bE0072 ile reddedilir: List sonsuz boyutlu, varyant Box<List> gibi bir dolaylama ister
  • cDerlenir, ama çalışma zamanında herhangi bir Cons düğümü oluşturmak stack'i taşırır ve panic olur
  • dE0382 ile reddedilir, çünkü iç List henüz tanımlanırken Cons içine move edilir
Açıklama:Bir enum'un boyutu derleme zamanında sonlu ve bilinir olmalıdır. Cons(i32, List) içinde List'i doğrudan sakladığı için List'in boyutu kendine bağlı olurdu; derleyici bunu E0072 ile reddeder (rustc 1.98'de ölçüldü). Özyinelemeli parçayı bir işaretçinin arkasına almak (Cons(i32, Box<List>)) varyanta sabit 8 byte'lık bir alan verir; enum'un tamamı 64-bit hedefte 16 byte ölçülür. Burada çalışma zamanı diye bir aşama yoktur, program tip denetimini hiç geçmez.
Rs Memory Smart Pointers UnsafeZorluk 2
use std::mem::size_of;
fn main() {
    println!("{} {}", size_of::<Option<Box<i32>>>(), size_of::<Option<i32>>());
}

64-bit bir hedefte bu ne yazdırır?
  • a16 8: Option, Box işaretçisinin önüne 8 byte'lık etiket koyar, i32 4 byte'lık etiket alır
  • b16 16: her Option ayrı bir discriminant taşır, bu yüzden ikisi de yükünden bir kelime büyüktür
  • c8 4: Option<i32> tamsayının işaret bitini None işareti olarak yeniden kullanır
  • d8 8: None, Box'ın hiç üretmediği null işaretçiyi kullanır, i32 ayrı etiket ister
Açıklama:Ölçüm: 8 8. Box<i32> null olamayan bir işaretçidir; derleyici bu yasak bit desenini ("niche") None'ı temsil etmek için kullanır, dolayısıyla Option<Box<i32>> tam bir işaretçi genişliğindedir. i32 2^32 bit deseninin hepsini kullandığından None için boş değer yoktur; discriminant ayrı bir byte'a gider ve 4 byte hizalama nedeniyle toplam 8 byte'a yuvarlanır. Aynı niche numarası &T ve Rc<T> için de geçerlidir.
Rs Memory Smart Pointers UnsafeZorluk 2
use std::rc::Rc;
fn main() {
    let a = Rc::new(String::from("x"));
    let b = Rc::clone(&a);
    let v = vec![a.clone(), a.clone()];
    println!("{} {}", Rc::strong_count(&a), Rc::ptr_eq(&a, &b));
    drop(v);
    drop(b);
    println!("{}", Rc::strong_count(&a));
}

Ne yazdırılır?
  • a3 true sonra 0: orijinal a sayılmaz, b düşünce son referans serbest kalır
  • b4 false sonra 1: Rc::clone String'i yeni bir tahsise kopyalar, işaretçiler bu nedenle ayrışır
  • c1 true sonra 1: sayaç ayrı sahipleri sayar, v ise aynı sahibe iki tutamaç tutar
  • d4 true sonra 1: clone'lar ortak sayacı artırır, düşünce sayaç geri iner
Açıklama:Ölçülen çıktı: 4 true / 1. Rc::clone (ve bir Rc üzerinde .clone()) String'i asla kopyalamaz; strong sayacı artırır ve aynı heap bloğuna bir işaretçi daha verir, Rc::ptr_eq'nin true olmasının nedeni budur. a, b ve v'nin iki elemanı dört strong tutamaçtır. v düşünce ikisi, b düşünce biri bırakılır ve a sayaç 1 ile tek başına kalır. Değerin kendisi ancak bu son sayaç 0'a inince serbest bırakılır.
Rs Memory Smart Pointers UnsafeZorluk 2
use std::cell::RefCell;
fn main() {
    let c = RefCell::new(vec![1]);
    let r1 = c.borrow();
    let r2 = c.borrow();
    println!("{} {}", r1.len(), r2.len());
    let m = c.borrow_mut();
    println!("{}", m.len());
}

Bu çalıştırıldığında ne olur?
  • a1 1 sonra 1 yazar: RefCell tek thread'de kaldığı sürece her türlü ödünç karışımına izin verir
  • bDerleme hatası E0502: borrow_mut özel erişim isterken r1 ve r2 hâlâ yaşıyor
  • c1 1 yazar, sonra iki Ref guard'ı hâlâ yaşadığından already borrowed ile panic olur
  • d1 1 sonra 1 yazar: borrow_mut çağrılır çağrılmaz r1 ve r2 otomatik olarak bırakılır
Açıklama:Ölçüm: 1 1 yazılır, sonra program RefCell already borrowed ile panic olur. RefCell ödünç kurallarını derleme zamanından çalışma zamanına taşır: bir ödünç sayacı tutar ve borrow_mut hiçbir Ref ya da RefMut guard'ının yaşamadığını kontrol eder. r1 ve r2 main'in sonuna kadar yaşadığından kontrol başarısız olur ve panic gelir. try_borrow_mut panic yerine Err döndürür; guard'ları borrow_mut'tan önce düşürmek (ya da bir blok içine almak) kodu sorunsuz çalıştırır.
Rs Memory Smart Pointers UnsafeZorluk 1
use std::cell::Cell;
fn bump(c: &Cell<u32>) {
    c.set(c.get() + 1);
}
fn main() {
    let c = Cell::new(1);
    bump(&c);
    bump(&c);
    println!("{} {} {}", c.get(), c.take(), c.get());
}

Bu ne yazdırır?
  • aDerleme hatası E0596: bump paylaşımlı referans alır, set bunun üzerinden değiştiremez
  • b3 3 3: take değerin bir kopyasını döndürür ve hücreyi değiştirmeden bırakır
  • c3 3 0: set &Cell üzerinden çalışır, take değeri u32::default() ile değişir
  • d1 1 0: paylaşımlı referans üzerinden yapılan değişiklikler bump dönünce atılır
Açıklama:Ölçüm: 3 3 0. Cell<T> iç değişebilirliğin en basit biçimidir: set ve get &self alır, dolayısıyla değeri değiştirmek için düz bir &Cell<u32> yeter. Çalışma zamanı ödünç bayrağı da yoktur, çünkü get değeri kopyalayıp dışarı verir, set yenisini kopyalayıp içeri koyar (Cell::get'in T: Copy istemesinin nedeni budur). take mevcut değeri döndürüp yerine Default::default() koyar; u32 için bu 0'dır, son 0 oradan gelir.
Rs Memory Smart Pointers UnsafeZorluk 2
struct D(&'static str);
impl Drop for D {
    fn drop(&mut self) { println!("drop {}", self.0); }
}
fn main() {
    let a = D("a");
    let b = D("b");
    std::mem::forget(a);
    drop(b);
    println!("end");
}

Tam çıktı nedir?
  • adrop b sonra end: forget a'nın destructor'ını asla çalıştırmaz, drop b'ninkini hemen çalıştırır
  • bdrop b, end, drop a: forget a'nın destructor'ını yalnızca main'in sonuna erteler
  • cdrop a, drop b, end: iki çağrı da argümanını tüketir, bir değeri tüketmek Drop çalıştırır
  • dDerleme hatası E0382: Drop tipleri unutulamaz, a forget sonrası move edilmiş sayılır
Açıklama:Ölçülen çıktı: drop b / end. std::mem::drop kelimenin tam anlamıyla fn drop<T>(_x: T) {}'dir: sahipliği alır ve değerin orada kapsam dışına çıkmasına izin verir, bu yüzden b'nin destructor'ı o satırda çalışır. std::mem::forget de sahipliği alır ama destructor'ı bilerek hiç çalıştırmaz; drop a hiç yazılmaz ve a'nın sahip olduğu heap belleği sızardı. Rust'ta sızdırmak güvenli sayılır (bellek güvensizliği yoktur), forget'in unsafe bir fonksiyon olmamasının nedeni tam olarak budur.

3750 soruluk Backend bankasında kendini sına.

Mülakata başla