| Crates.io | rds2rust |
| lib.rs | rds2rust |
| version | 0.1.39 |
| created_at | 2025-11-03 02:31:46.916108+00 |
| updated_at | 2025-12-20 01:36:41.060502+00 |
| description | A pure Rust library for reading and writing R's RDS (R Data Serialization) files without requiring an R runtime. |
| homepage | |
| repository | |
| max_upload_size | |
| id | 1913796 |
| size | 687,733 |
A pure Rust library for reading and writing R's RDS (R Data Serialization) files without requiring an R runtime. Inspired by rds2cpp, which provides similar functionality with a C++ implementation.
Add this to your Cargo.toml:
[dependencies]
rds2rust = "0.1"
use rds2rust::{read_rds, RObject};
use std::fs;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Read RDS file (automatically decompresses if gzipped)
let data = fs::read("data.rds")?;
let obj = read_rds(&data)?;
// Pattern match on R object type
match obj {
RObject::DataFrame(df) => {
println!("Data frame with {} columns", df.columns.len());
// Access a specific column
if let Some(RObject::Real(values)) = df.columns.get("temperature") {
println!("Temperature values: {:?}", values);
}
}
RObject::Integer(vec) => {
println!("Integer vector: {:?}", vec);
}
_ => println!("Other R object type"),
}
Ok(())
}
use rds2rust::{write_rds, RObject};
use std::fs;
use std::sync::Arc;
fn main() -> Result<(), Box<dyn std::error::Error>> {
// Create an R object (e.g., a character vector)
let obj = RObject::Character(vec![
Arc::from("hello"),
Arc::from("world"),
].into());
// Serialize to RDS format (automatically gzip compressed)
let rds_data = write_rds(&obj)?;
// Write to file
fs::write("output.rds", rds_data)?;
Ok(())
}
use rds2rust::{read_rds, RObject};
use std::sync::Arc;
// Read a data frame
let data = std::fs::read("iris.rds")?;
let obj = read_rds(&data)?;
if let RObject::DataFrame(df) = obj {
// Access columns by name
let sepal_length = df.columns.get("Sepal.Length");
let species = df.columns.get("Species");
// Access row names
println!("First row name: {}", df.row_names[0]);
// Iterate over columns
for (name, values) in &df.columns {
println!("Column: {}", name);
}
}
use rds2rust::{read_rds, RObject};
let data = std::fs::read("factor.rds")?;
let obj = read_rds(&data)?;
if let RObject::Factor(factor) = obj {
// Check if it's an ordered factor
if factor.ordered {
println!("Ordered factor with {} levels", factor.levels.len());
}
// Get level labels
for level in &factor.levels {
println!("Level: {}", level);
}
// Get values (1-based indices into levels)
for &index in &factor.values {
if index > 0 && index <= factor.levels.len() as i32 {
let level = &factor.levels[(index - 1) as usize];
println!("Value: {}", level);
}
}
}
use rds2rust::{read_rds, RObject};
use std::sync::Arc;
let data = std::fs::read("model.rds")?;
let obj = read_rds(&data)?;
// S3 objects
if let RObject::S3Object(s3) = obj {
println!("S3 class: {:?}", s3.class);
// Access base object
match s3.base.as_ref() {
RObject::List(elements) => {
println!("S3 object is a list with {} elements", elements.len());
}
_ => {}
}
// Access additional attributes
if let Some(desc) = s3.attributes.get("description") {
println!("Description: {:?}", desc);
}
}
// S4 objects
if let RObject::S4Object(s4) = obj {
println!("S4 class: {:?}", s4.class);
// Access slots
if let Some(slot_value) = s4.slots.get("data") {
println!("Data slot: {:?}", slot_value);
}
}
use rds2rust::{read_rds, write_rds};
use std::fs;
// Read an RDS file
let input_data = fs::read("input.rds")?;
let obj = read_rds(&input_data)?;
// Process the data...
// (modify the object as needed)
// Write back to RDS format
let output_data = write_rds(&obj)?;
fs::write("output.rds", output_data)?;
// Verify roundtrip
let obj2 = read_rds(&output_data)?;
assert_eq!(obj, obj2);
The RObject enum represents all possible R object types:
pub enum RObject {
Null,
Integer(VectorData<i32>),
Real(VectorData<f64>),
Logical(VectorData<Logical>),
Character(VectorData<Arc<str>>),
Symbol(Arc<str>),
Raw(VectorData<u8>),
Complex(VectorData<Complex>),
List(Vec<RObject>),
Pairlist(Vec<PairlistElement>),
Language { function: Box<RObject>, args: Vec<PairlistElement> },
Expression(Vec<RObject>),
Closure { formals: Box<RObject>, body: Box<RObject>, environment: Box<RObject> },
Environment { enclosing: Box<RObject>, frame: Box<RObject>, hashtab: Box<RObject> },
Promise { value: Box<RObject>, expression: Box<RObject>, environment: Box<RObject> },
Special { name: Arc<str> },
Builtin { name: Arc<str> },
Bytecode { code: Box<RObject>, constants: Box<RObject>, expr: Box<RObject> },
DataFrame(Box<DataFrameData>),
Factor(Box<FactorData>),
S3Object(Box<S3ObjectData>),
S4Object(Box<S4ObjectData>),
Namespace(Vec<Arc<str>>),
GlobalEnv,
BaseEnv,
EmptyEnv,
MissingArg,
UnboundValue,
Shared(Arc<RwLock<RObject>>),
WithAttributes { object: Box<RObject>, attributes: Attributes },
}
R's special values are represented as:
RObject::NA_INTEGER constant (i32::MIN)Logical::Na enum variantf64::is_nan()f64::INFINITY and f64::NEG_INFINITYf64::NANrds2rust includes several memory optimizations for efficient data processing:
Arc<str> for automatic deduplicationThese optimizations provide 20-50% memory reduction for typical RDS files while maintaining zero API overhead.
use rds2rust::read_rds;
use std::fs::File;
use std::io::Read;
// For very large files, read in chunks if needed
let mut file = File::open("large.rds")?;
let mut buffer = Vec::new();
file.read_to_end(&mut buffer)?;
let obj = read_rds(&buffer)?;
use std::sync::Arc;
use rds2rust::RObject;
// Wrap in Arc for cheap cloning
let obj = Arc::new(read_rds(&data)?);
// Clone is cheap (just increments reference count)
let obj2 = Arc::clone(&obj);
Current version: 0.1.36
Test coverage: extensive test suite covering core R object types and roundtrips
Completed phases:
Licensed under: