Desert (DEserialize SERialize Traits) is a lightweight and extendable serialization and deserialization framework for Rust, inspired by serde. It aims to provide a modular and type-safe way to serialize and deserialize data structures without relying on runtime reflection, making it ideal for use in projects with strong type systems like Fungal.
While serde is a mature and highly efficient serialization framework in Rust, there are a few reasons why we chose to create desert as a simpler, custom alternative:
-
Alignment with Fungal's Future Goals:
desertis designed with Fungal's type system and language philosophy in mind. The goal is to eventually self-host Fungal's compiler, which means all supporting libraries and tools, including serialization, should be built natively in Fungal itself. By creating a stripped-down version now, we can more easily portdesertto Fungal in the future. -
Minimalism and Extendability:
desertstarts with a minimal core that focuses only on the functionality currently needed. Unlikeserde, which supports a wide variety of formats and complex features,desertprovides a lightweight and straightforward base that can be extended incrementally as the project evolves. This makes it easier to understand, maintain, and adapt to Fungal's unique requirements. -
Control and Customization: By implementing our own serialization framework, we maintain full control over its design and behavior. This allows us to add domain-specific optimizations or features tailored to Fungal's needs without being constrained by the design decisions or complexity of a third-party library.
-
Procedural Macros and Future Integration: While
serdeuses procedural macros for deriving traits likeSerializeandDeserialize,deserttakes a similar but simplified approach. This allows us to experiment with different ways of implementing serialization in Rust and prepare for deeper integration into Fungal's compilation and macro system. -
Zero External Serialization Dependencies:
deserthas zero dependencies on external serialization libraries (serde, toml, etc.), ensuring complete independence. The only external dependency is theimcrate for immutable collections, which aligns with Fungal's immutable-first philosophy. All serialization logic is custom-built and self-contained.
- Binary Serialization: Efficient binary format with magic bytes, version headers, and varint encoding
- 35 type implementations: primitives, collections, tuples (2-12 elements), smart pointers
- Cross-platform safe (usize/isize handled correctly)
- Security features: collection size limits, UTF-8 validation
- Returns
im::Vector<u8>for immutability (Fungal-aligned)
- TOML Support: Human-readable configuration format
- NEW: Complete TOML deserialization (parsing)
- TOML serialization (writing)
- Ready for Cap package manager integration
- Trait-based Serialization and Deserialization: Define
SerializeandDeserializetraits that can be implemented for various types - Procedural Macros for Auto-Derivation: Automatically derive serialization and deserialization implementations using procedural macros
- Extensible Serialization Formats: JSON, TOML, and binary formats with the ability to add more as needed
- Pure Core + Imperative Shell: Efficient internal mutation with immutable external interface
- Portable to Fungal: No proc macros, unsafe code, or complex lifetimes in core implementation
Add desert to your Cargo.toml:
[dependencies]
desert = { git = "https://github.com/fungal-lang/desert" }
desert_macros = { git = "https://github.com/fungal-lang/desert", package = "desert_macros" }use desert::binary::{serialize_binary, deserialize_binary};
// Serialize a value
let value = vec![1, 2, 3, 4, 5];
let bytes = serialize_binary(&value)?;
// Deserialize it back
let decoded: Vec<i32> = deserialize_binary(&bytes.iter().copied().collect::<Vec<_>>())?;
assert_eq!(value, decoded);use desert::{Serialize, JsonSerializer};
use std::sync::Arc;
#[derive(Serialize)]
struct MyStruct {
name: String,
age: i32,
}
fn main() {
let my_struct = MyStruct {
name: "Alice".to_string(),
age: 30,
};
let serializer = Arc::new(JsonSerializer::new());
let serialized = my_struct.serialize(&serializer);
println!("Serialized JSON: {}", serialized);
}use desert::parse_toml;
fn main() {
let toml_str = r#"
[package]
name = "my-project"
version = "0.1.0"
"#;
// Parse TOML without needing serde derives!
let value = parse_toml(toml_str).unwrap();
let package = value.get_required("package").unwrap();
let name = package.get_string("name").unwrap();
let version = package.get_string("version").unwrap();
println!("Project: {} v{}", name, version);
}For more examples, see examples/binary_serialization_examples.rs and the Binary Serialization Guide.
Contributing
We welcome contributions! Please check out How to Contribute for more details on how to get started.