Skip to content

Commit eeebbbe

Browse files
feat: rust recursive
1 parent d82dab7 commit eeebbbe

File tree

2 files changed

+187
-0
lines changed

2 files changed

+187
-0
lines changed
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
---
2+
slug: "deep-flattening-in-rust-using-recursive-types"
3+
title: "Deep Flattening in Rust - Using Recursive Types "
4+
date: 2024-12-31T00:00:00+00:00
5+
authors: [joel-medicala, abeeshake]
6+
tags: [ rust ]
7+
---
8+
9+
10+
### Deep Flattening in Rust: A Recursive Adventure
11+
12+
Flattening nested data structures is a common problem in programming. However, flattening structures with an arbitrary depth—like nested `Vec`s within `Vec`s—can be tricky. Rust, with its strong type system and trait-based polymorphism, allows us to implement elegant solutions to such problems. In this post, we'll explore a recursive approach to deep flattening in Rust using traits, type inference, and iterators.
13+
14+
#### The Goal
15+
16+
Given a deeply nested structure, such as:
17+
18+
```rust
19+
let nested_vec = vec![
20+
vec![vec![1, 2, 3], vec![4, 5]],
21+
vec![vec![6], vec![7, 8, 9]],
22+
];
23+
```
24+
25+
Our goal is to flatten it into:
26+
27+
```rust
28+
let flattened = vec![1, 2, 3, 4, 5, 6, 7, 8, 9];
29+
```
30+
31+
The depth of nesting is not fixed—it could be `Vec<Vec<Vec<T>>>`, `Vec<Vec<Vec<Vec<T>>>>`, or deeper.
32+
33+
---
34+
35+
### TL;DR: high level idea
36+
37+
Rust’s iterators and traits allow us to create a type-safe, recursive implementation to handle deep flattening. The solution uses three key components:
38+
39+
1. **The **``** Trait**: A recursive trait defining how to flatten iterators.
40+
2. **Base and Recursive Implementations**: Separate implementations for handling the base case (non-nested items) and recursive case (nested items).
41+
3. **A Wrapper Struct**: A helper type to simplify type inference.
42+
43+
---
44+
45+
### Implementation
46+
47+
The fun part lies in using Rust's types as in recursive way.
48+
49+
#### The `DeepFlattenIteratorOf` Trait
50+
51+
This trait defines the recursive structure of our flattening logic:
52+
53+
```rust
54+
pub trait DeepFlattenIteratorOf<Depth, T> {
55+
type DeepFlatten: Iterator<Item = T>;
56+
fn deep_flatten(this: Self) -> Self::DeepFlatten;
57+
}
58+
```
59+
60+
- `Depth` tracks the nesting level.
61+
- `T` is the type of the innermost element.
62+
- `DeepFlatten` is the resulting iterator after flattening.
63+
64+
#### Base Case: No Nesting
65+
66+
The base condition stops recursion when the structure is already flat:
67+
68+
```rust
69+
impl<I: Iterator> DeepFlattenIteratorOf<(), I::Item> for I {
70+
type DeepFlatten = Self;
71+
72+
fn deep_flatten(this: Self) -> Self::DeepFlatten {
73+
this
74+
}
75+
}
76+
```
77+
78+
Here, when `Depth` is `()`, no further flattening is needed, and the iterator is returned as-is.
79+
80+
#### Recursive Case: Flatten Nested Items
81+
82+
For nested structures, the recursion continues until reaching the base case:
83+
84+
```rust
85+
impl<Depth, I, T> DeepFlattenIteratorOf<(Depth,), T> for I
86+
where
87+
Flatten<I>: DeepFlattenIteratorOf<Depth, T>,
88+
I: Iterator,
89+
I::Item: IntoIterator,
90+
{
91+
type DeepFlatten = <Flatten<I> as DeepFlattenIteratorOf<Depth, T>>::DeepFlatten;
92+
93+
fn deep_flatten(this: Self) -> Self::DeepFlatten {
94+
DeepFlattenIteratorOf::deep_flatten(this.flatten())
95+
}
96+
}
97+
```
98+
99+
- `Flatten<I>` handles one level of flattening.
100+
- The recursion continues until it reaches the base case.
101+
102+
#### Wrapper Struct for Type Inference
103+
104+
The `DeepFlatten` struct simplifies type inference by wrapping the recursive logic:
105+
106+
```rust
107+
pub struct DeepFlatten<Depth, I, T>
108+
where
109+
I: DeepFlattenIteratorOf<Depth, T>,
110+
{
111+
inner: I::DeepFlatten,
112+
}
113+
114+
impl<I: Iterator> DeepFlattenExt for I {}
115+
```
116+
117+
This allows users to call the `.deep_flatten()` method directly:
118+
119+
```rust
120+
pub trait DeepFlattenExt: Iterator + Sized {
121+
fn deep_flatten<Depth, T>(self) -> DeepFlatten<Depth, Self, T>
122+
where
123+
Self: DeepFlattenIteratorOf<Depth, T>,
124+
{
125+
DeepFlatten {
126+
inner: DeepFlattenIteratorOf::deep_flatten(self),
127+
}
128+
}
129+
}
130+
```
131+
132+
#### Iterator Implementation for `DeepFlatten`
133+
134+
Finally, the iterator implementation allows seamless iteration over flattened items:
135+
136+
```rust
137+
impl<Depth, I, T> Iterator for DeepFlatten<Depth, I, T>
138+
where
139+
I: DeepFlattenIteratorOf<Depth, T>,
140+
{
141+
type Item = T;
142+
143+
fn next(&mut self) -> Option<Self::Item> {
144+
self.inner.next()
145+
}
146+
}
147+
```
148+
149+
---
150+
151+
### Example Usage
152+
153+
Here’s how you can use the `deep_flatten` method to flatten nested structures:
154+
155+
```rust
156+
fn main() {
157+
let nested_vec = vec![
158+
vec![vec![1, 2, 3], vec![4, 5]],
159+
vec![vec![6], vec![7, 8, 9]],
160+
];
161+
162+
let flattened: Vec<i32> = nested_vec.into_iter().deep_flatten().collect();
163+
164+
assert_eq!(flattened, vec![1, 2, 3, 4, 5, 6, 7, 8, 9]);
165+
166+
println!("Flattened result: {:?}", flattened);
167+
}
168+
```
169+
170+
---
171+
172+
This code gist was prepared by Joel is [available on rust playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=dbd26d3c4e89abbf50cde86dec296cd7)!
173+
174+
Thanks Joel once again for bringing light to this pattern!
175+
That's a wrap for this year!
176+
177+
See you in next year!

blog/authors.yml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,13 @@ abeeshake:
77
socials:
88
x: twistin456
99
github: TwistingTwists
10+
11+
12+
joel-medicala:
13+
name: Joel Medicala
14+
url: https://github.com/JoeruCodes
15+
image_url: https://github.com/JoeruCodes.png
16+
page: true
17+
socials:
18+
x: JoeruCodes
19+
github: JoeruCodes

0 commit comments

Comments
 (0)