פתרון ממשק בינארי¶
הקוד הבא בקובץ record.c משתמש בכותרות של סי כדי לחשב את הפריסה בפועל.
#include <stdint.h>
#include <stddef.h>
#include <string.h>
struct Record { uint8_t tag; uint64_t value; };
size_t record_size(void) { return sizeof(struct Record); }
size_t record_value_offset(void) { return offsetof(struct Record, value); }
uint64_t record_sum(const struct Record *r) { return r->tag + r->value; }
size_t text_len(const char *p) { return strlen(p); }
הקוד הבא הוא תוכנית מלאה שמקושרת לארכיון librecord.a באותו אופן שבו בנינו את הדוגמה בהרצאה.
use std::ffi::{c_char, CString, NulError};
use std::mem::{offset_of, size_of};
#[repr(C)]
struct Record { tag: u8, value: u64 }
unsafe extern "C" {
fn record_size() -> usize;
fn record_value_offset() -> usize;
fn record_sum(r: *const Record) -> u64;
fn text_len(p: *const c_char) -> usize;
}
fn length(text: &str) -> Result<usize, NulError> {
let owned = CString::new(text)?;
Ok(unsafe { text_len(owned.as_ptr()) })
}
fn main() {
assert_eq!(unsafe { record_size() }, size_of::<Record>());
assert_eq!(unsafe { record_value_offset() }, offset_of!(Record, value));
let record = Record { tag: 3, value: 10 };
assert_eq!(unsafe { record_sum(&record) }, 13);
assert_eq!(length("abc"), Ok(3));
assert!(length("a\0b").is_err());
}
הבדיקות תופסות חוסר התאמה בבנייה המסוימת. ההתחייבות המתמשכת מגיעה מהשילוב של repr(C), טיפוסים נכונים ואותו ABI, ולא מהתוצאה המספרית של בדיקה אחת. המחרוזת נשארת בבעלות owned עד אחרי החזרה מ־text_len, ולכן המצביע אינו תלוי בערך זמני שכבר הושמד.