FFI wrapper
- Rust kursu
- İleri seviye
- Ders 81/81
- 7 dk
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
use std::ffi::{c_char, c_int, c_long, CString};
fn main() {
// buraya yaz
}Örnek çözüm
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", &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
let mut son: *mut c_char = std::ptr::____();
let n = unsafe { strtol(c.as_ptr(), &mut son, taban) };
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 |