or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

cpp-core.mdflexbuffers.mdgo.mdindex.mdjava.mdjavascript.mdpython.mdrust.mdschema-compiler.mdverification.md
tile.json

rust.mddocs/

Rust

Rust implementation of FlatBuffers providing memory-safe access with zero-cost abstractions and integration with Rust's ownership model and type system. The Rust binding leverages Rust's compile-time guarantees while maintaining the performance benefits of zero-copy deserialization.

Capabilities

Installation and Dependencies

Add FlatBuffers to your Rust project using Cargo.

# Cargo.toml
[dependencies]
flatbuffers = "25.2.10"

# Optional features
[dependencies.flatbuffers]
version = "25.2.10"
features = ["std"]  # Default feature for std library support

# For no_std environments
[dependencies.flatbuffers]
version = "25.2.10"
default-features = false

# With serde support for serialization interop
[dependencies.flatbuffers]  
version = "25.2.10"
features = ["serialize"]
// Core imports
use flatbuffers::{FlatBufferBuilder, Vector, WIPOffset, Table, Verifiable};

// Additional common imports
use std::collections::HashMap;
use std::io::{Read, Write};

FlatBufferBuilder

Main builder struct for constructing FlatBuffer data in Rust applications.

pub struct FlatBufferBuilder<'a> {
    // Internal fields (private)
}

impl<'a> FlatBufferBuilder<'a> {
    /**
     * Create new FlatBufferBuilder with default capacity
     * @return New builder instance
     */
    pub fn new() -> Self;
    
    /**
     * Create FlatBufferBuilder with specific initial capacity
     * @param initial_size Initial buffer size in bytes
     * @return New builder instance
     */
    pub fn with_capacity(initial_size: usize) -> Self;
    
    /**
     * Reset builder for reuse, keeping allocated memory
     */
    pub fn reset(&mut self);
    
    /**
     * Get current buffer size
     * @return Size in bytes
     */
    pub fn len(&self) -> usize;
    
    /**
     * Check if buffer is empty
     * @return True if empty
     */
    pub fn is_empty(&self) -> bool;
    
    /**
     * Create string and return its offset
     * @param s String slice to store
     * @return WIPOffset to string
     */
    pub fn create_string(&mut self, s: &str) -> WIPOffset<&str>;
    
    /**
     * Create vector from slice
     * @param items Slice of items to store
     * @return WIPOffset to vector
     */
    pub fn create_vector<T: Copy + 'static>(&mut self, items: &[T]) -> WIPOffset<Vector<'_, T>>;
    
    /**
     * Create vector of strings
     * @param items Slice of string offsets
     * @return WIPOffset to vector
     */
    pub fn create_vector_of_strings(&mut self, items: &[WIPOffset<&str>]) -> WIPOffset<Vector<'_, WIPOffset<&str>>>;
    
    /**
     * Create vector of tables
     * @param items Slice of table offsets
     * @return WIPOffset to vector
     */
    pub fn create_vector_of_tables<T>(&mut self, items: &[WIPOffset<T>]) -> WIPOffset<Vector<'_, WIPOffset<T>>>;
    
    /**
     * Start building a table
     * @param num_fields Number of fields in table
     */
    pub fn start_table(&mut self, num_fields: usize);
    
    /**
     * Add field to current table
     * @param slot Field slot number
     * @param value Value to add
     * @param default_value Default value for comparison
     */
    pub fn push_slot<T: Copy + PartialEq>(&mut self, slot: usize, value: T, default_value: T);
    
    /**
     * Add offset field to current table
     * @param slot Field slot number  
     * @param offset Offset value
     */
    pub fn push_slot_always<T>(&mut self, slot: usize, offset: WIPOffset<T>);
    
    /**
     * End table construction
     * @return WIPOffset to completed table
     */
    pub fn end_table(&mut self) -> WIPOffset<Table>;
    
    /**
     * Finish buffer with root table
     * @param root Root table offset
     * @param file_identifier Optional 4-byte file identifier
     */
    pub fn finish<T>(&mut self, root: WIPOffset<T>, file_identifier: Option<&str>);
    
    /**
     * Finish buffer with size prefix
     * @param root Root table offset  
     * @param file_identifier Optional 4-byte file identifier
     */
    pub fn finish_size_prefixed<T>(&mut self, root: WIPOffset<T>, file_identifier: Option<&str>);
    
    /**
     * Get finished buffer data
     * @return Slice containing FlatBuffer data
     */
    pub fn finished_data(&self) -> &[u8];
    
    /**
     * Get finished buffer data as mutable slice
     * @return Mutable slice containing FlatBuffer data
     */
    pub fn finished_data_mut(&mut self) -> &mut [u8];
    
    /**
     * Take ownership of finished buffer
     * @return Vector containing FlatBuffer data
     */
    pub fn collapse(self) -> Vec<u8>;
}

impl<'a> Default for FlatBufferBuilder<'a> {
    fn default() -> Self {
        Self::new()
    }
}

Usage Example:

use flatbuffers::FlatBufferBuilder;

// Create builder
let mut builder = FlatBufferBuilder::new();

// Create string
let name = builder.create_string("Player");

// Create vector
let scores = vec![100, 200, 300, 400, 500];
let scores_offset = builder.create_vector(&scores);

// Create table
builder.start_table(3);
builder.push_slot_always(0, name);           // name field (slot 0)
builder.push_slot(1, 42i32, 0);              // level field (slot 1)
builder.push_slot_always(2, scores_offset);  // scores field (slot 2)
let player = builder.end_table();

// Finish buffer
builder.finish(player, None);

// Get binary data
let buffer = builder.finished_data();
println!("Buffer size: {} bytes", buffer.len());

WIPOffset Type

Work-in-progress offset type providing type safety during buffer construction.

/**
 * Work-in-progress offset type for type-safe buffer construction
 * @param T Type being offset to
 */
pub struct WIPOffset<T> {
    // Internal offset value (private)
}

impl<T> WIPOffset<T> {
    /**
     * Get raw offset value
     * @return Offset as u32
     */
    pub fn value(&self) -> u32;
}

impl<T> Copy for WIPOffset<T> {}
impl<T> Clone for WIPOffset<T> {
    fn clone(&self) -> Self { *self }
}

// Conversion traits
impl<T> From<WIPOffset<T>> for u32 {
    fn from(offset: WIPOffset<T>) -> u32 {
        offset.value()
    }
}

Table Access

Table access functionality for reading FlatBuffer data with Rust's memory safety guarantees.

/**
 * Table type for accessing FlatBuffer table data
 */
#[derive(Debug, Clone, Copy)]
pub struct Table<'a> {
    pub buf: &'a [u8],  // Buffer containing table data
    pub loc: usize,     // Location of table in buffer
}

impl<'a> Table<'a> {
    /**
     * Create new table accessor
     * @param buf Buffer containing data
     * @param loc Table location in buffer
     * @return Table instance
     */
    pub fn new(buf: &'a [u8], loc: usize) -> Self;
    
    /**
     * Get field offset from vtable
     * @param vtable_offset Offset in vtable
     * @return Field offset or None if not present
     */
    pub fn get<T: Copy>(&self, vtable_offset: usize) -> Option<T>;
    
    /**
     * Get field with default value
     * @param vtable_offset Offset in vtable
     * @param default_value Default value if field missing
     * @return Field value or default
     */
    pub fn get_with_default<T: Copy>(&self, vtable_offset: usize, default_value: T) -> T;
    
    /**
     * Get string at field offset
     * @param vtable_offset Offset in vtable
     * @return Optional string slice
     */
    pub fn get_string(&self, vtable_offset: usize) -> Option<&'a str>;
    
    /**
     * Get vector at field offset
     * @param vtable_offset Offset in vtable  
     * @return Optional vector
     */
    pub fn get_vector<T: Copy>(&self, vtable_offset: usize) -> Option<Vector<'a, T>>;
    
    /**
     * Get table at field offset
     * @param vtable_offset Offset in vtable
     * @return Optional table
     */
    pub fn get_table(&self, vtable_offset: usize) -> Option<Table<'a>>;
    
    /**
     * Get union table at field offset
     * @param vtable_offset Offset in vtable
     * @return Optional table for union access
     */
    pub fn get_union_table(&self, vtable_offset: usize) -> Option<Table<'a>>;
}

Vector Type

Vector type for accessing FlatBuffer vector data with iterator support.

/**
 * Vector type for accessing FlatBuffer vector data
 * @param T Element type
 */
#[derive(Debug, Clone, Copy)]
pub struct Vector<'a, T: Copy> {
    buf: &'a [u8],      // Buffer containing vector data
    pos: usize,         // Position of vector in buffer
    len: usize,         // Number of elements
    _phantom: std::marker::PhantomData<T>,
}

impl<'a, T: Copy> Vector<'a, T> {
    /**
     * Get vector length
     * @return Number of elements
     */
    pub fn len(&self) -> usize;
    
    /**
     * Check if vector is empty
     * @return True if empty
     */
    pub fn is_empty(&self) -> bool;
    
    /**
     * Get element at index
     * @param index Element index
     * @return Element value
     */
    pub fn get(&self, index: usize) -> T;
    
    /**
     * Safe element access with bounds checking
     * @param index Element index
     * @return Optional element value
     */  
    pub fn safe_get(&self, index: usize) -> Option<T>;
    
    /**
     * Get raw bytes of vector data
     * @return Byte slice containing vector elements
     */
    pub fn bytes(&self) -> &'a [u8];
    
    /**
     * Convert to slice (zero-copy when possible)
     * @return Slice view of vector data
     */
    pub fn as_slice(&self) -> &'a [T];
    
    /**
     * Create iterator over vector elements
     * @return Iterator over elements
     */
    pub fn iter(&self) -> VectorIter<'a, T>;
}

// Iterator implementation
impl<'a, T: Copy> IntoIterator for Vector<'a, T> {
    type Item = T;
    type IntoIter = VectorIter<'a, T>;
    
    fn into_iter(self) -> Self::IntoIter {
        self.iter()
    }
}

/**
 * Iterator over vector elements
 */
pub struct VectorIter<'a, T: Copy> {
    vector: Vector<'a, T>,
    index: usize,
}

impl<'a, T: Copy> Iterator for VectorIter<'a, T> {
    type Item = T;
    
    fn next(&mut self) -> Option<Self::Item> {
        if self.index < self.vector.len() {
            let item = self.vector.get(self.index);
            self.index += 1;
            Some(item)
        } else {
            None
        }
    }
    
    fn size_hint(&self) -> (usize, Option<usize>) {
        let remaining = self.vector.len() - self.index;
        (remaining, Some(remaining))
    }
}

impl<'a, T: Copy> ExactSizeIterator for VectorIter<'a, T> {}

Generated Code Structure

When using flatc --rust, the compiler generates Rust code following these patterns.

// Example generated Rust (from monster.fbs):

use flatbuffers::{FlatBufferBuilder, Vector, WIPOffset, Table};

// Vec3 struct definition
#[derive(Debug, Clone, PartialEq)]
pub struct Vec3 {
    pub x: f32,
    pub y: f32,
    pub z: f32,
}

impl Default for Vec3 {
    fn default() -> Self {
        Self { x: 0.0, y: 0.0, z: 0.0 }
    }
}

impl Vec3 {
    /**
     * Pack Vec3 into FlatBufferBuilder
     * @param builder FlatBuffer builder
     * @param vec3 Vec3 instance to pack
     * @return Offset to packed struct
     */
    pub fn pack(builder: &mut FlatBufferBuilder, vec3: &Vec3) -> WIPOffset<Vec3> {
        builder.prep(4, 12);
        builder.write_f32(vec3.z);
        builder.write_f32(vec3.y);
        builder.write_f32(vec3.x);
        WIPOffset::new(builder.offset())
    }
    
    /**
     * Unpack Vec3 from buffer
     * @param buf Buffer containing data
     * @param pos Position in buffer
     * @return Vec3 instance
     */
    pub fn unpack(buf: &[u8], pos: usize) -> Self {
        use flatbuffers::byte_slice_to_f32;
        Self {
            x: byte_slice_to_f32(&buf[pos..pos+4]),
            y: byte_slice_to_f32(&buf[pos+4..pos+8]),
            z: byte_slice_to_f32(&buf[pos+8..pos+12]),
        }
    }
}

// Monster table definition
#[derive(Debug, Clone, Copy)]
pub struct Monster<'a> {
    pub _tab: Table<'a>,
}

impl<'a> Monster<'a> {
    /**
     * Get Monster from buffer
     * @param buf Buffer containing FlatBuffer data
     * @return Monster instance
     */
    pub fn get_root(buf: &'a [u8]) -> Self {
        let offset = flatbuffers::read_scalar::<u32>(&buf[0..4]) as usize;
        Self {
            _tab: Table::new(buf, offset),
        }
    }
    
    /**
     * Initialize Monster from table
     * @param table Table containing Monster data
     * @return Monster instance
     */
    pub fn init_from_table(table: Table<'a>) -> Self {
        Self { _tab: table }
    }
    
    /**
     * Get position as Vec3
     * @return Optional Vec3
     */
    pub fn pos(&self) -> Option<Vec3> {
        self._tab.get::<u32>(4).map(|offset| {
            Vec3::unpack(self._tab.buf, self._tab.loc + offset as usize)
        })
    }
    
    /**
     * Get mana value
     * @return Mana value with default
     */
    pub fn mana(&self) -> i16 {
        self._tab.get_with_default(6, 150)
    }
    
    /**
     * Get HP value
     * @return HP value with default
     */
    pub fn hp(&self) -> i16 {
        self._tab.get_with_default(8, 100)
    }
    
    /**
     * Get name string
     * @return Optional name string
     */
    pub fn name(&self) -> Option<&'a str> {
        self._tab.get_string(10)
    }
    
    /**
     * Get inventory vector
     * @return Optional inventory vector
     */
    pub fn inventory(&self) -> Option<Vector<'a, u8>> {
        self._tab.get_vector(14)
    }
}

// Monster builder functions
pub struct MonsterArgs<'a> {
    pub pos: Option<&'a Vec3>,
    pub mana: i16,
    pub hp: i16,
    pub name: Option<WIPOffset<&'a str>>,
    pub inventory: Option<WIPOffset<Vector<'a, u8>>>,
}

impl<'a> Default for MonsterArgs<'a> {
    fn default() -> Self {
        Self {
            pos: None,
            mana: 150,
            hp: 100,
            name: None,
            inventory: None,
        }
    }
}

/**
 * Create Monster table
 * @param builder FlatBuffer builder
 * @param args Monster arguments
 * @return Offset to created Monster
 */
pub fn create_monster<'a>(
    builder: &mut FlatBufferBuilder<'a>,
    args: &MonsterArgs<'a>,
) -> WIPOffset<Monster<'a>> {
    builder.start_table(6);
    
    if let Some(pos) = args.pos {
        let pos_offset = Vec3::pack(builder, pos);
        builder.push_slot_always(0, pos_offset);
    }
    
    builder.push_slot(1, args.mana, 150);
    builder.push_slot(2, args.hp, 100);
    
    if let Some(name) = args.name {
        builder.push_slot_always(3, name);
    }
    
    if let Some(inventory) = args.inventory {
        builder.push_slot_always(5, inventory);
    }
    
    builder.end_table()
}

Error Handling and Validation

Rust FlatBuffers provides comprehensive error handling and buffer validation.

use flatbuffers::{InvalidFlatbuffer, Verifiable, Verifier, VerifierOptions};

/**
 * Error types for FlatBuffer operations
 */
#[derive(Debug, Clone)]
pub enum FlatBufferError {
    InvalidBuffer(String),
    OutOfBounds { index: usize, len: usize },
    InvalidUtf8(std::str::Utf8Error),
    VerificationFailed(InvalidFlatbuffer),
}

impl std::fmt::Display for FlatBufferError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::InvalidBuffer(msg) => write!(f, "Invalid buffer: {}", msg),
            Self::OutOfBounds { index, len } => {
                write!(f, "Index {} out of bounds for length {}", index, len)
            }
            Self::InvalidUtf8(err) => write!(f, "Invalid UTF-8: {}", err),
            Self::VerificationFailed(err) => write!(f, "Verification failed: {:?}", err),
        }
    }
}

impl std::error::Error for FlatBufferError {}

/**
 * Safe buffer access with error handling
 * @param buf Buffer to validate and access
 * @return Result containing Monster or error
 */
pub fn safe_get_monster(buf: &[u8]) -> Result<Monster<'_>, FlatBufferError> {
    // Basic validation
    if buf.len() < 4 {
        return Err(FlatBufferError::InvalidBuffer("Buffer too short".to_string()));
    }
    
    // Get root with verification
    let monster = Monster::get_root(buf);
    
    // Additional validation can be added here
    Ok(monster)
}

/**
 * Verify buffer integrity
 * @param buf Buffer to verify
 * @return Result indicating success or verification error
 */
pub fn verify_buffer(buf: &[u8]) -> Result<(), FlatBufferError> {
    let opts = VerifierOptions::default();
    let mut verifier = Verifier::new(&opts, buf);
    
    // Verify the buffer structure
    Monster::run_verifier(&mut verifier, 0)
        .map_err(FlatBufferError::VerificationFailed)
}

File I/O and Serialization Integration

Working with files and Rust's serialization ecosystem.

use std::fs::File;
use std::io::{Read, Write, BufReader, BufWriter};
use std::path::Path;

/**
 * Save FlatBuffer to file
 * @param builder Completed FlatBuffer builder
 * @param path Output file path
 * @return Result indicating success or IO error
 */
pub fn save_flatbuffer<P: AsRef<Path>>(
    builder: &FlatBufferBuilder,
    path: P,
) -> std::io::Result<()> {
    let mut file = BufWriter::new(File::create(path)?);
    file.write_all(builder.finished_data())?;
    file.flush()
}

/**
 * Load FlatBuffer from file
 * @param path Input file path
 * @return Result containing buffer data or IO error
 */
pub fn load_flatbuffer<P: AsRef<Path>>(path: P) -> std::io::Result<Vec<u8>> {
    let mut file = BufReader::new(File::open(path)?);
    let mut buffer = Vec::new();
    file.read_to_end(&mut buffer)?;
    Ok(buffer)
}

// Serde integration (with "serialize" feature)
#[cfg(feature = "serialize")]
mod serde_support {
    use serde::{Deserialize, Serialize};
    use super::*;
    
    /**
     * Wrapper for serde serialization of FlatBuffer data
     */
    #[derive(Serialize, Deserialize)]
    pub struct SerializableMonster {
        pub name: Option<String>,
        pub hp: i16,
        pub mana: i16,
        pub pos: Option<Vec3>,
        pub inventory: Vec<u8>,
    }
    
    impl<'a> From<Monster<'a>> for SerializableMonster {
        fn from(monster: Monster<'a>) -> Self {
            Self {
                name: monster.name().map(|s| s.to_string()),
                hp: monster.hp(),
                mana: monster.mana(),
                pos: monster.pos(),
                inventory: monster.inventory()
                    .map(|v| v.iter().collect())
                    .unwrap_or_default(),
            }
        }
    }
}

Complete Usage Example:

use flatbuffers::FlatBufferBuilder;
// Import generated types
use crate::generated::{Monster, MonsterArgs, Vec3, create_monster};

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create monster
    let mut builder = FlatBufferBuilder::new();
    
    // Create components
    let name = builder.create_string("Dragon");
    let pos = Vec3 { x: 1.0, y: 2.0, z: 3.0 };
    let inventory_data = vec![1u8, 2, 3, 4, 5];
    let inventory = builder.create_vector(&inventory_data);
    
    // Build monster using args struct
    let monster_args = MonsterArgs {
        pos: Some(&pos),
        mana: 200,
        hp: 150,
        name: Some(name),
        inventory: Some(inventory),
        ..Default::default()
    };
    
    let monster = create_monster(&mut builder, &monster_args);
    
    // Finish buffer
    builder.finish(monster, None);
    let buffer = builder.finished_data();
    
    // Read the data back
    let read_monster = Monster::get_root(buffer);
    
    println!("Name: {:?}", read_monster.name());
    println!("HP: {}", read_monster.hp());
    println!("Mana: {}", read_monster.mana());
    
    if let Some(position) = read_monster.pos() {
        println!("Position: {}, {}, {}", position.x, position.y, position.z);
    }
    
    if let Some(inventory) = read_monster.inventory() {
        println!("Inventory size: {}", inventory.len());
        for (i, item) in inventory.iter().enumerate() {
            println!("Item {}: {}", i, item);
        }
    }
    
    // Save to file
    std::fs::write("monster.bin", buffer)?;
    
    // Load from file
    let loaded_buffer = std::fs::read("monster.bin")?;
    let loaded_monster = Monster::get_root(&loaded_buffer);
    println!("Loaded name: {:?}", loaded_monster.name());
    
    Ok(())
}