What is Deserialization?
Deserialization in Rust is the process of taking data stored in a specific format, like JSON, TOML, YAML, or even binary, and converting it into a native Rust data structure such as a struct, enum, or Vec.
In simple words, when data comes from outside your program (like from a file, an API, or a database), it usually arrives as text or binary. Your Rust program cannot directly work with this raw data, so deserialization helps by translating that data into meaningful Rust types that you can use directly in your code.
Why Deserialization is Important In Rust?
- Reading API Responses: Data received from a web API is usually in JSON. Deserialization converts it into a Rust struct so you can use it easily.
- Processing Configuration Files: TOML or YAML config files are deserialized into structs to access values like app_name, port, or debug.
- Handling Binary Data: Serialized binary data can be deserialized back into Rust objects for use in applications like games, databases, or caching systems.
How to Perform Deserialization in Rust
Deserialization in Rust becomes very easy and powerful with the help of the Serde library. It is the most widely used framework for serialization and deserialization in the Rust ecosystem.
Serde works as a bridge between external data (like JSON, TOML, or YAML) and your Rust data structures (like structs, enums, and collections).
First we need to set up Serde and a format-specific crate, for example, serde_json if you’re working with JSON.
Add Serde and serde_json to Your Project
Open your project’s Cargo.toml file and add the following dependencies:
[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
- serde is the core library that provides serialization and deserialization capabilities.
- serde_json is a helper crate for working specifically with JSON data.
Then run the following command to download and include these dependencies:
cargo build
Define a Rust Struct for Your Data
Before deserializing, you need a Rust structure that matches the shape of the data you want to read. Suppose you’re working with user data coming from an API in JSON format:
{
"name": "Alice",
"age": 25,
"email": "alice@example.com"
}
We can represent this data with a simple Rust struct:
use serde::Deserialize;
#[derive(Deserialize, Debug)]
struct User {
name: String,
age: u8,
email: String,
}
- The #[derive(Deserialize)] attribute automatically generates the code needed to convert external data into this struct.
- Field names in the struct should match the keys in your JSON.
Deserialize JSON Data into a Struct
Now, let’s write the main logic to convert a JSON string into a User struct:
Example: Deserializing JSON Data
use serde::Deserialize;
use serde_json;
#[derive(Deserialize, Debug)]
struct User {
name: String,
age: u8,
email: String,
}
fn main() {
let json_data = r#"
{
"name": "Alice",
"age": 25,
"email": "alice@example.com"
}
"#;
// Convert the JSON string into a Rust struct
let user: User = serde_json::from_str(json_data).expect("Failed to deserialize JSON");
// Use the deserialized data just like normal Rust variables
println!("Name: {}", user.name);
println!("Age: {}", user.age);
println!("Email: {}", user.email);
}
Output:
Name: Alice
Age: 25
Email: alice@example.com
Explanation:
- serde_json::from_str() function reads the JSON text and automatically converts it into a User struct.
- If the data matches the structure correctly, you will get a fully usable Rust object.
Handling Deserialization Errors
When we are working with real-world data, we can’t always trust the incoming data. Sometimes it might be incomplete, wrongly formatted, or use the wrong data types, so the error handling is a crucial part of deserialization, and it prevents your program from crashing.
Example: Error Handling During Deserialization
Imagine we’re receiving user data in JSON format from a web API. But sometimes the API sends “age”: “thirty” instead of a number. If we try to directly deserialize it into a u8 (a number), Rust will throw an error, unless we handle it properly.
For example:
use serde::Deserialize;
use serde_json;
#[derive(Deserialize, Debug)]
struct User {
name: String,
age: u8,
email: String,
}
fn main() {
// This JSON has a mistake: "age" is a string, but we expect a number
let broken_json = r#"
{
"name": "Alice",
"age": "thirty",
"email": "alice@example.com"
}
"#;
// Using match to handle both success and failure cases
match serde_json::from_str::<User>(broken_json) {
Ok(user) => {
println!("Successfully deserialized: {:?}", user);
}
Err(err) => {
println!("Oops! Something went wrong while deserializing:");
println!(" {}", err);
}
}
}
Output:
Oops! Something went wrong while deserializing:
invalid type: string "thirty", expected u8 at line 4 column 21
Deserializing TOML Data In Rust
Deserialization allows us to read this TOML file and convert it directly into a Rust struct, making the data easy and safe to use in our program.
Example: Deserializing TOML Data
app_name = "MyApp"
version = "1.0.0"
debug = true
This file stores some basic details about your application. Now, let’s learn how to load this data into a Rust program
Rust Code: Reading TOML Into a Struct
use serde::Deserialize;
use toml;
#[derive(Deserialize, Debug)]
struct Config {
app_name: String,
version: String,
debug: bool,
}
fn main() {
// Simulating TOML content as a string (in real projects, you might read this from a file)
let toml_content = r#"
app_name = "MyApp"
version = "1.0.0"
debug = true
"#;
// Deserialize: Convert TOML string into a Rust struct
let config: Config = toml::from_str(toml_content)
.expect("Failed to deserialize TOML data");
// Use the struct fields like normal Rust variables
println!("App Name: {}", config.app_name);
println!("Version: {}", config.version);
println!("Debug Mode: {}", config.debug);
}
Output:
App Name: MyApp
Version: 1.0.0
Debug Mode: true
Deserializing Binary Data in Rust
Serialization and deserialization are not only useful for text-based formats like JSON or TOML, they’re equally important when working with binary data.
Binary formats are compact, fast to read/write, and used in game development, networking, file storage, and embedded systems where performance matters.
Deserialization of binary data takes raw binary bytes (Vec<u8>) and converts them back into a Rust struct or data type you can use in your program.
Example: Deserializing Binary Data with bincode
Add this to Cargo.toml:
[dependencies]
bincode = "1.3"
serde = { version = "1.0", features = ["derive"] }
Rust Code: Converting Binary Bytes Back into a Struct
use serde::{Serialize, Deserialize};
use bincode;
#[derive(Serialize, Deserialize, Debug)]
struct Point {
x: i32,
y: i32,
}
fn main() {
// Imagine we received this binary data from a file or network
let binary_data: Vec<u8> = vec![8, 0, 0, 0, 12, 0, 0, 0];
// Deserialize: Convert binary bytes back into a `Point` struct
let point: Point = bincode::deserialize(&binary_data)
.expect("Failed to deserialize binary data");
// Use the deserialized struct just like normal
println!("Deserialized Point: {:?}", point);
println!("X coordinate: {}", point.x);
println!("Y coordinate: {}", point.y);
}
Output:
Deserialized Point: Point { x: 8, y: 12 }
X coordinate: 8
Y coordinate: 12
You can see, #[derive(Serialize, Deserialize)] enables Point to be converted to and from binary automatically. Then bincode::deserialize(): Takes the binary data (Vec<u8>) and rebuilds the original Point struct.
Advanced Example: Nested Deserialization
Rust’s serde library handles nested structures automatically and elegantly, making it easy to map complex JSON into structured Rust types.
Nested deserialization is the process of converting structured data (like JSON with objects inside objects) into nested Rust structs.
Example: Deserializing a Nested JSON into Rust Structs
use serde::Deserialize;
#[derive(Deserialize, Debug)]
struct Address {
city: String,
zip_code: String,
}
#[derive(Deserialize, Debug)]
struct User {
name: String,
age: u8,
address: Address, //Nested struct
}
fn main() {
let json_data = r#"
{
"name": "Alice",
"age": 30,
"address": {
"city": "Wonderland",
"zip_code": "12345"
}
}
"#;
// Deserialize JSON into a nested struct
let user: User = serde_json::from_str(json_data)
.expect("Failed to deserialize nested JSON");
// Use the deserialized data
println!("Name: {}", user.name);
println!("Age: {}", user.age);
println!("City: {}", user.address.city);
println!("ZIP Code: {}", user.address.zip_code);
// Or just print the entire struct for debugging
println!("\n Full Struct: {:?}", user);
}
Output:
Name: Alice
Age: 30
City: Wonderland
ZIP Code: 12345
Full Struct: User { name: "Alice", age: 30, address: Address { city: "Wonderland", zip_code: "12345" } }
Learn Other Topics About Rust
- What are threads in Rust?
- What is concurrency in Rust?
- What is error handling in Rust?
- What is an HashMap in Rust?
- What is an Iterator in Rust?
- What is Mutex in Rust?
- What is Async in Rust?

M.Sc. (Information Technology). I explain AI, AGI, Programming and future technologies in simple language. Founder of BoxOfLearn.com.