C Library Example
#ifndef TEXT_ANALYSIS_H
#define TEXT_ANALYSIS_H
#include <stddef.h>
#include <stdbool.h>
typedef struct TextAnalyst TextAnalyst;
typedef struct {
const char* start;
size_t length;
size_t index;
} Token;
typedef enum {
TA_OK = 0,
TA_ERR_NULL_POINTER,
TA_ERR_OUT_OF_MEMORY,
TA_ERR_OTHER,
} TAError;
/* Return `false` to indicate that no token was found. */
typedef bool (*Tokenizer)(Token* token, void* extra_context);
typedef bool (*TokenCallback)(void* user_context, Token* token, void* result);
/* TextAnalyst constructor */
TextAnalyst* ta_new(void);
/* TextAnalyst destructor */
void ta_free(TextAnalyst* ta);
/* Resets state to clear the current document */
void ta_reset(TextAnalyst* ta);
/* Use custom tokenizer (defaults to whitespace) */
void ta_set_tokenizer(TextAnalyst* ta, Tokenizer* func);
TAError ta_set_text(TextAnalyst* ta, const char* text, size_t len, bool make_copy);
/* Apply `callback` to each token */
size_t ta_foreach_token(const TextAnalyst* ta, const TokenCallback* callback, void* user_context);
/* Get human-readable error message */
const char* ta_error_string(TAError error);
#endif /* TEXT_ANALYSIS_H */
C libraries will hide their implementation details with a void* argument.
Consider this header file of a natural language processing library that hides
the TextAnalyst and Analysis types.
This can be emulated in Rust with a type similar to this:
#![allow(unused)] fn main() { #[repr(C)] pub struct TextAnalyst { _private: [u8; 0], } }
Exercise: Ask learners to wrap this library.
Suggested Solution
#![allow(unused)] fn main() { // ffi.rs use std::ffi::c_char; use std::os::raw::c_void; #[repr(C)] pub struct TextAnalyst { _private: [u8; 0], } #[repr(C)] #[derive(Debug, Clone, Copy)] pub struct Token { pub start: *const c_char, pub length: usize, pub index: usize, } #[repr(C)] #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum TAError { Ok = 0, NullPointer = 1, OutOfMemory = 2, Other = 3, } pub type Tokenizer = Option< unsafe extern "C" fn(token: *mut Token, extra_context: *mut c_void) -> bool, >; pub type TokenCallback = Option< unsafe extern "C" fn( user_context: *mut c_void, token: *mut Token, result: *mut c_void, ) -> bool, >; unsafe extern "C" { pub fn ta_new() -> *mut TextAnalyst; pub fn ta_free(ta: *mut TextAnalyst); pub fn ta_reset(ta: *mut TextAnalyst); pub fn ta_set_tokenizer(ta: *mut TextAnalyst, func: *const Tokenizer); pub fn ta_set_text( ta: *mut TextAnalyst, text: *const c_char, len: usize, make_copy: bool, ) -> TAError; pub fn ta_foreach_token( ta: *const TextAnalyst, callback: *const TokenCallback, user_context: *mut c_void, ) -> usize; pub fn ta_error_string(error: TAError) -> *const c_char; } }