CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-flatbuffers

Memory efficient cross-platform serialization library with zero-copy deserialization supporting 15+ programming languages.

Pending
Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

SecuritybySnyk

Pending

The risk profile of this skill

Overview
Eval results
Files

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(())
}

docs

cpp-core.md

flexbuffers.md

go.md

index.md

java.md

javascript.md

python.md

rust.md

schema-compiler.md

verification.md

tile.json