ProgrammingRust Backend/API Developer

How to implement serialization and deserialization of data in Rust? What typical problems arise when integrating with external formats (JSON, TOML), and how to ensure the safety of structure conversion?

Pass interviews with Hintsage AI assistant

Answer.

In Rust, serialization and deserialization of data are quite common tasks, especially when integrating with web services, databases, or message exchanges between components. The most popular library for this is serde, which has become the de facto standard for serialization in Rust.

Background

Rust developers faced the need for flexible and performant serialization. Initially, there were many specialized solutions, but serde offered convenient integration with derive macros, a flexible scheme for supporting various formats (JSON, CBOR, BSON, TOML, YAML, and others).

Problem

It is necessary to guarantee the correct transformation of Rust structures into data exchange formats (for example, JSON), and vice versa, without losing type safety and avoiding "silent errors". Problems often arise due to a mismatch in the data structure or attempts to serialize unsupported types.

Solution

To serialize structures, you need to implement the Serialize and Deserialize traits, usually with #[derive(Serialize, Deserialize)]. For non-standard cases, you can implement these traits manually or use attributes that control the serialization scheme.

Example code:

use serde::{Serialize, Deserialize}; #[derive(Serialize, Deserialize, Debug)] struct Person { name: String, age: u8, } fn main() { let data = Person { name: "Bob".into(), age: 32 }; let json = serde_json::to_string(&data).unwrap(); println!("{}", json); let obj: Person = serde_json::from_str(&json).unwrap(); println!("{:?}", obj); }

Key features:

  • derive automatically implements the needed traits for most cases.
  • serde supports multiple formats and is easily extensible.
  • It is possible to precisely describe mapping through attributes (rename, default, skip, etc.).

Trick Questions.

How to serialize/deserialize Option fields or fields with default values?

You can use #[serde(default)] to specify a default value or #[serde(skip_serializing_if = "Option::is_none")], to avoid serializing None.

Example code:

#[derive(Serialize, Deserialize)] struct Config { #[serde(default)] timeout: Option<u32>, }

Is it possible to serialize structures with custom fields or computed values (like fn get_hash())?

Yes, but such fields should be marked with #[serde(skip)], if they should not be serialized, or you can implement Serialize/Deserialize manually if computed values need to be serialized.

What happens if a structure in Rust does not match the fields of a JSON object (missing a field or an extra one appears)?

By default, serde ignores extra fields in JSON (unless strict mode is enabled), and it will throw an error if required fields are missing in the structure (if #[serde(default)] is not specified).

Typical Errors and Anti-Patterns

  • Expecting strict format conformity without specifying default/skip — leads to parsing errors.
  • Serializing structures with private or invalid values without additional checks.
  • Implementing Serialize/Deserialize manually without necessity.

Real-Life Example

Negative Case

An external JSON comes with extra and missing fields, and the structure does not use #[serde(default)] ,

Pros:

  • Fast integration.

Cons:

  • Crashes with the first incorrect JSON.
  • Difficult to extend.

Positive Case

Uses #[serde(default)] and #[serde(skip_serializing_if)], all fields are validated, extra ones are ignored.

Pros:

  • Resilience to schema changes.
  • Simple backward compatibility.

Cons:

  • Slightly more boilerplate for configuration.
  • Not all non-standard cases are covered automatically, sometimes manual implementation is required.