Loading pageā¦
Rust walkthroughs
Loading pageā¦
serde::Serializer::collect_seq and serialize_seq for collection serialization?serde::Serializer::collect_seq is a convenience method that automatically iterates over a collection and serializes each element, while serialize_seq is the lower-level primitive that returns a SerializeSeq type requiring manual element-by-element serialization. The key distinction is control versus convenience: collect_seq handles the entire sequence in one call, internally creating a SerializeSeq, serializing each element, and calling end(), while serialize_seq returns a builder that lets you serialize elements one at a time with serialize_element, enabling custom logic between elements or conditional element inclusion. collect_seq is implemented in terms of serialize_seqāit's syntactic sugar that assumes you want to serialize every element in order. Use collect_seq when you have an iterable and want simple serialization; use serialize_seq when you need to control which elements are serialized, add custom logic between elements, or interleave serialization with other operations.
use serde::Serialize;
#[derive(Serialize)]
struct Data {
numbers: Vec<i32>,
names: Vec<String>,
}
fn main() {
let data = Data {
numbers: vec
![1, 2, 3, 4, 5],
names: vec
!["alice".to_string(), "bob".to_string()],
};
let json = serde_json::to_string(&data).unwrap();
println!("{}", json);
}collect_seq handles the common case of serializing all elements.
use serde::{ser::SerializeSeq, Serialize, Serializer};
struct CustomSequence {
items: Vec<i32>,
}
impl Serialize for CustomSequence {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
// serialize_seq returns a builder
let mut seq = serializer.serialize_seq(Some(self.items.len()))?;
// Manually serialize each element
for item in &self.items {
seq.serialize_element(item)?;
}
// Must call end() to complete
seq.end()
}
}
fn main() {
let custom = CustomSequence { items: vec
![1, 2, 3] };
let json = serde_json::to_string(&custom).unwrap();
println!("{}", json);
}serialize_seq gives you control over each element's serialization.
use serde::{ser::SerializeSeq, Serialize, Serializer};
struct Numbers {
values: Vec<i32>,
}
impl Serialize for Numbers {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
// These two approaches are equivalent:
// Approach 1: collect_seq (convenience)
serializer.collect_seq(&self.values)
// Approach 2: serialize_seq (manual)
// let mut seq = serializer.serialize_seq(Some(self.values.len()))?;
// for item in &self.values {
// seq.serialize_element(item)?;
// }
// seq.end()
}
}collect_seq internally uses serialize_seq with automatic iteration.
use serde::{Serialize, Serializer, ser::SerializeSeq};
struct FilteredSequence {
items: Vec<i32>,
threshold: i32,
}
impl Serialize for FilteredSequence {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
// Must use serialize_seq to conditionally include elements
let filtered: Vec<_> = self.items.iter().filter(|&&x| x > self.threshold).collect();
let mut seq = serializer.serialize_seq(Some(filtered.len()))?;
for item in &filtered {
seq.serialize_element(item)?;
}
seq.end()
}
}
fn main() {
let filtered = FilteredSequence {
items: vec
![1, 5, 10, 3, 8, 2],
threshold: 4,
};
let json = serde_json::to_string(&filtered).unwrap();
println!("{}", json); // [5, 10, 8]
}serialize_seq allows filtering elements before serialization.
use serde::{Serialize, Serializer, ser::SerializeSeq};
use std::collections::HashMap;
struct MapToSeq {
map: HashMap<String, i32>,
}
impl Serialize for MapToSeq {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
// Serialize a map as a sequence of key-value pairs
let mut seq = serializer.serialize_seq(Some(self.map.len()))?;
// Custom logic: sort by value before serializing
let mut pairs: Vec<_> = self.map.iter().collect();
pairs.sort_by_key(|(_, v)| *v);
for (k, v) in pairs {
// Serialize each entry with custom structure
seq.serialize_element(&(k, v))?;
}
seq.end()
}
}serialize_seq enables custom transformation during serialization.
use serde::{Serialize, Serializer, ser::SerializeSeq};
struct WithHint {
items: Vec<String>,
}
impl Serialize for WithHint {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
// Provide size hint for efficient allocation
let mut seq = serializer.serialize_seq(Some(self.items.len()))?;
for item in &self.items {
seq.serialize_element(item)?;
}
seq.end()
}
}
struct WithoutHint {
items: Vec<String>,
}
impl Serialize for WithoutHint {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
// No size hint - serializer may need to reallocate
let mut seq = serializer.serialize_seq(None)?;
for item in &self.items {
seq.serialize_element(item)?;
}
seq.end()
}
}serialize_seq accepts an optional size hint; collect_seq derives it from ExactSizeIterator.
use serde::{ser::{self, SerializeSeq, Serializer}, Error};
use std::collections::VecDeque;
struct SimpleSerializer;
impl Serializer for SimpleSerializer {
type Ok = ();
type Error = ser::Error;
type SerializeSeq = SeqSerializer;
// ... other associated types
fn serialize_seq(self, len: Option<usize>) -> Result<Self::SerializeSeq, Self::Error> {
println!("Starting sequence with length hint: {:?}", len);
Ok(SeqSerializer { count: 0 })
}
// collect_seq is provided by default implementation
// fn collect_seq<I>(self, iter: I) -> Result<Self::Ok, Self::Error>
// where I: IntoIterator, I::Item: Serialize
}
struct SeqSerializer {
count: usize,
}
impl SerializeSeq for SeqSerializer {
type Ok = ();
type Error = ser::Error;
fn serialize_element<T>(&mut self, value: &T) -> Result<(), Self::Error>
where
T: Serialize + ?Sized,
{
self.count += 1;
println!("Element {}: {:?}", self.count, serde_json::to_string(value));
Ok(())
}
fn end(self) -> Result<Self::Ok, Self::Error> {
println!("Sequence ended with {} elements", self.count);
Ok(())
}
}Custom serializers must implement serialize_seq; collect_seq gets a default implementation.
use serde::{Serialize, Serializer};
fn serialize_various<S: Serializer>(serializer: S) -> Result<S::Ok, S::Error> {
// Vec
let vec = vec
![1, 2, 3];
serializer.collect_seq(&vec)?;
// Array
let arr = [1, 2, 3];
serializer.collect_seq(&arr)?;
// Slice
let slice: &[i32] = &[1, 2, 3];
serializer.collect_seq(slice)?;
// Iterator (must be Clone for collect_seq)
let iter = vec
![1, 2, 3].into_iter();
// Can't use collect_seq directly on iterator without collecting first
Ok(())
}collect_seq works with any IntoIterator where items implement Serialize.
use serde::{Serialize, Serializer, ser::SerializeSeq};
use std::io::{self, Write};
struct StreamingSequence<W> {
writer: W,
}
impl<W: Write> StreamingSequence<W> {
fn serialize_items<S>(&mut self, items: impl Iterator<Item = i32>, serializer: S) -> Result<(), S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_seq(None)?;
for item in items {
// Process each item before serializing
self.writer.write_all(format!("Processing: {}\n", item).as_bytes()).unwrap();
seq.serialize_element(&item)?;
}
seq.end()?;
Ok(())
}
}serialize_seq enables interleaving serialization with other operations.
use serde::{Serialize, Serializer, ser::SerializeSeq};
struct Matrix {
rows: Vec<Vec<i32>>,
}
impl Serialize for Matrix {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut outer = serializer.serialize_seq(Some(self.rows.len()))?;
for row in &self.rows {
// Use collect_seq for inner sequences
outer.serialize_element(&row)?;
}
outer.end()
}
}
// Alternative: collect_seq for both
impl Serialize for Matrix {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
// Fully automatic - no control
serializer.collect_seq(&self.rows)
}
}Mix serialize_seq and collect_seq as needed for nested structures.
use serde::{Serialize, Serializer, ser::SerializeSeq};
struct LargeSequence {
items: Vec<i32>,
}
impl Serialize for LargeSequence {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
// With size hint from serialize_seq(Some(len))
let mut seq = serializer.serialize_seq(Some(self.items.len()))?;
for item in &self.items {
seq.serialize_element(item)?;
}
seq.end()
}
}
// collect_seq uses the iterator's size hint
// Vec's iterator provides exact size, so collect_seq is equally efficientBoth can be equally efficient; the key is providing size hints when available.
use serde::{Serialize, Serializer, ser::SerializeSeq, ser::Error};
struct ValidatedSequence {
items: Vec<i32>,
}
impl Serialize for ValidatedSequence {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_seq(Some(self.items.len()))?;
for (i, item) in self.items.iter().enumerate() {
// Validate during serialization
if *item < 0 {
return Err(S::Error::custom(format!(
"Negative value at index {}",
i
)));
}
seq.serialize_element(item)?;
}
seq.end()
}
}serialize_seq enables validation or error handling per element.
use serde::{Serialize, Serializer, ser::SerializeSeq};
struct PrefixedSequence {
prefix: String,
items: Vec<String>,
}
impl Serialize for PrefixedSequence {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
let mut seq = serializer.serialize_seq(Some(self.items.len()))?;
for item in &self.items {
// Transform each element during serialization
let prefixed = format!("{}:{}", self.prefix, item);
seq.serialize_element(&prefixed)?;
}
seq.end()
}
}
fn main() {
let data = PrefixedSequence {
prefix: "user".to_string(),
items: vec
!["alice".to_string(), "bob".to_string()],
};
let json = serde_json::to_string(&data).unwrap();
println!("{}", json); // ["user:alice","user:bob"]
}Transform elements during serialization with serialize_seq.
// Method comparison
/*
| Aspect | collect_seq | serialize_seq |
|---------------------|-------------------------|------------------------|
| Control level | Low (automatic) | High (manual) |
| Code complexity | Simple | More boilerplate |
| Size hint | From iterator | Explicit optional |
| Element filtering | Not possible | Possible |
| Custom per-element | Not possible | Possible |
| Must call end() | No (handled) | Yes (required) |
| Error handling | Propagated | Full control |
| Use case | Simple sequences | Complex sequences |
*/Choose based on the complexity of your serialization needs.
API comparison:
| Method | Returns | Responsibility |
|--------|---------|----------------|
| collect_seq | Result<Ok, Error> | Handles everything |
| serialize_seq | Result<SerializeSeq, Error> | Caller manages elements and end() |
When to use each:
// Use collect_seq for simple cases
impl Serialize for Simple {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
serializer.collect_seq(&self.items) // One-liner
}
}
// Use serialize_seq when you need:
// - Conditional element inclusion
// - Custom transformation per element
// - Validation during serialization
// - Interleaved operations
impl Serialize for Complex {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where S: Serializer {
let mut seq = serializer.serialize_seq(None)?;
for item in &self.items {
if self.should_include(item) {
seq.serialize_element(&self.transform(item))?;
}
}
seq.end()
}
}Key insight: collect_seq and serialize_seq represent two levels of abstraction for the same underlying operationāserializing a sequence of values. serialize_seq is the primitive: it returns a SerializeSeq builder that requires you to call serialize_element for each item and end() when finished. This gives you complete control: you can skip elements, transform them, add logging between elements, or validate as you go. collect_seq is convenience layer: implemented in serde's default Serializer trait using serialize_seq internally, it handles the iteration, element serialization, and end() call automatically. The trade-off is between simplicity and controlāif you need any form of custom logic during sequence serialization, serialize_seq is necessary; if you're just passing through an iterable's values unchanged, collect_seq is cleaner. Note that collect_seq does derive a size hint from Iterator::size_hint, so for collections that implement ExactSizeIterator, the performance is equivalent to manually calling serialize_seq(Some(len)). The choice is architectural: collect_seq is the "happy path" default, while serialize_seq is the escape hatch for when the default behavior doesn't match your requirements.