Пример библиотеки на C
#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;
/* Возвращает `false`, если токен не найден. */
typedef bool (*Tokenizer)(Token* token, void* extra_context);
typedef bool (*TokenCallback)(void* user_context, Token* token, void* result);
/* Конструктор TextAnalyst */
TextAnalyst* ta_new(void);
/* Деструктор TextAnalyst */
void ta_free(TextAnalyst* ta);
/* Сбрасывает состояние, очищая текущий документ */
void ta_reset(TextAnalyst* ta);
/* Использовать собственный токенизатор (по умолчанию — пробельные символы) */
void ta_set_tokenizer(TextAnalyst* ta, Tokenizer* func);
TAError ta_set_text(TextAnalyst* ta, const char* text, size_t len, bool make_copy);
/* Применяет `callback` к каждому токену */
size_t ta_foreach_token(const TextAnalyst* ta, const TokenCallback* callback, void* user_context);
/* Получает понятное человеку сообщение об ошибке */
const char* ta_error_string(TAError error);
#endif /* TEXT_ANALYSIS_H */
Библиотеки на C скрывают детали реализации с помощью аргумента void*.
Рассмотрим заголовочный файл библиотеки обработки естественного языка, которая скрывает типы TextAnalyst и Analysis.
Это можно эмулировать в Rust с помощью похожего типа:
#![allow(unused)]
fn main() {
// Copyright 2026 Google LLC
// SPDX-License-Identifier: Apache-2.0
#[repr(C)]
pub struct TextAnalyst {
_private: [u8; 0],
}
}
Упражнение: попросите слушателей написать обёртку для этой библиотеки.
Предлагаемое решение
#![allow(unused)]
fn main() {
// Copyright 2026 Google LLC
// SPDX-License-Identifier: Apache-2.0
// 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;
}
}