CodeBlanks Logo Icon CodeBlanks

FFI wrapper

Bir C kütüphanesi için güvenli bir wrapper yazarsın.

Bitirme görevi: FFI sarmalayıcı

C'nin strtol fonksiyonu bir metni verilen tabanda sayıya çevirir ve okumayı nerede bitirdiğini bir çıkış işaretçisiyle bildirir. Ham arayüz hata yapmaya çok açıktır; görev, onu güvenli bir Rust fonksiyonuyla sarmak:

unsafe extern "C" {
    fn strtol(s: *const c_char, son: *mut *mut c_char, taban: c_int) -> c_long;
}

fn sayiya(s: &str, taban: i32) -> Result<i64, String> { .. }

Güvenli arayüzün sorumlulukları: metni CString'e çevirmek (iç boş bayt hatası), işaretçinin ne kadar ilerlediğine bakarak geçersiz karakteri bulmak ve sonucu Rust türleriyle döndürmek.

Güvenli sarmalayıcı

offset_from, aynı ayırmanın içindeki iki işaretçi arasındaki eleman farkını verir:

fn sayiya(s: &str, taban: i32) -> Result<i64, String> {
    let c = CString::new(s).map_err(|_| String::from("metinde boş bayt var"))?;
    let mut son: *mut c_char = std::ptr::null_mut();
    // SAFETY: c geçerli bir C dizesi; son yazılabilir bir işaretçi.
    let n = unsafe { strtol(c.as_ptr(), &mut son, taban) };
    // SAFETY: strtol son'u c'nin içinde bir yere ayarlar.
    let okunan = unsafe { son.cast_const().offset_from(c.as_ptr()) } as usize;
    match s[okunan..].chars().next() {
        None if okunan > 0 => Ok(n as i64),
        None => Err(String::from("boş girdi")),
        Some(k) => Err(format!("geçersiz karakter: {k}")),
    }
}

Uygulama strtol sarmalayıcı

Yukarıdaki bildirimi ve güvenli sayiya fonksiyonunu yaz. sayiya("ff", 16) ve sayiya("101", 2) sonuçlarını unwrap ile, sayiya("12x", 10) sonucunu {:?} ile boşlukla yazdır.

Başlangıç kodu

main.rs
use std::ffi::{c_char, c_int, c_long, CString};

fn main() {
    // buraya yaz
}

Örnek çözüm

main.rs
use std::ffi::{c_char, c_int, c_long, CString};

unsafe extern "C" {
    fn strtol(s: *const c_char, son: *mut *mut c_char, taban: c_int) -> c_long;
}

fn sayiya(s: &str, taban: i32) -> Result<i64, String> {
    let c = CString::new(s).map_err(|_| String::from("metinde boş bayt var"))?;
    let mut son: *mut c_char = std::ptr::null_mut();
    // SAFETY: c geçerli bir C dizesi; son yazılabilir bir işaretçi.
    let n = unsafe { strtol(c.as_ptr(), &mut son, taban) };
    // SAFETY: strtol son'u c'nin içinde bir yere ayarlar.
    let okunan = unsafe { son.cast_const().offset_from(c.as_ptr()) } as usize;
    match s[okunan..].chars().next() {
        None if okunan > 0 => Ok(n as i64),
        None => Err(String::from("boş girdi")),
        Some(k) => Err(format!("geçersiz karakter: {k}")),
    }
}

fn main() {
    println!("{} {} {:?}", sayiya("ff", 16).unwrap(), sayiya("101", 2).unwrap(), sayiya("12x", 10));
}

Beklenen çıktı

255 5 Err("geçersiz karakter: x")

Soru ve cevaplar

Dersin sorularını cevaplarıyla birlikte inceleyebilirsin. Kendini denemek istersen dersi interaktif olarak çöz.

1 <code>strtol("12x", &amp;son, 10)</code> sonrasında <code>son</code> nereyi gösterir?

  • Başa
  • NULL'a
  • "x" karakterine (okumanın durduğu yer) Doğru cevap
  • Dizenin sonuna

Açıklama: Fark (son - baş) okunan bayt sayısıdır; metin uzunluğuna eşit değilse geçersiz karakter vardır.

2 Çıkış işaretçisi

Soru
let mut son: *mut c_char = std::ptr::____();
let n = unsafe { strtol(c.as_ptr(), &mut son, taban) };
Cevap
let mut son: *mut c_char = std::ptr::null_mut();
let n = unsafe { strtol(c.as_ptr(), &mut son, taban) };

Açıklama: null_mut boş bir *mut işaretçi oluşturur; strtol onu doldurur.

3 Girdi ve sonuç

Kavram Anlamı
("ff", 16) Ok(255)
("101", 2) Ok(5)
("12x", 10) geçersiz karakter: x
("", 10) boş girdi

İleri seviye bölümündeki tüm dersler