<?xml version="1.0" encoding="UTF-8"?>
<rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/"
    xmlns:atom="http://www.w3.org/2005/Atom" xmlns:media="http://search.yahoo.com/mrss/" version="2.0">
    <channel>
        
        <title>
            <![CDATA[ Anshul Sanghi - freeCodeCamp.org ]]>
        </title>
        <description>
            <![CDATA[ Browse thousands of programming tutorials written by experts. Learn Web Development, Data Science, DevOps, Security, and get developer career advice. ]]>
        </description>
        <link>https://www.freecodecamp.org/news/</link>
        <image>
            <url>https://cdn.freecodecamp.org/universal/favicons/favicon.png</url>
            <title>
                <![CDATA[ Anshul Sanghi - freeCodeCamp.org ]]>
            </title>
            <link>https://www.freecodecamp.org/news/</link>
        </image>
        <generator>Eleventy</generator>
        <lastBuildDate>Fri, 22 May 2026 17:39:14 +0000</lastBuildDate>
        <atom:link href="https://www.freecodecamp.org/news/author/anshulsanghi/rss.xml" rel="self" type="application/rss+xml" />
        <ttl>60</ttl>
        
            <item>
                <title>
                    <![CDATA[ How to Blend Images in Rust Using Pixel Math ]]>
                </title>
                <description>
                    <![CDATA[ For anyone looking to learn about image processing as a programming niche, blending images is a very good place to start. It's one of the simplest yet most rewarding techniques when it comes to image processing. To help your intuition, it's best to i... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/how-to-blend-images-in-rust-using-pixel-math/</link>
                <guid isPermaLink="false">66cda9b4e220ecb31e3a2239</guid>
                
                    <category>
                        <![CDATA[ Rust ]]>
                    </category>
                
                    <category>
                        <![CDATA[ image processing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Mathematics ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Anshul Sanghi ]]>
                </dc:creator>
                <pubDate>Tue, 27 Aug 2024 10:25:56 +0000</pubDate>
                <media:content url="https://cdn.hashnode.com/res/hashnode/image/upload/v1724689572465/f03e4b74-1091-4673-af5b-c8827e74caf0.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>For anyone looking to learn about image processing as a programming niche, blending images is a very good place to start. It's one of the simplest yet most rewarding techniques when it comes to image processing.</p>
<p>To help your intuition, it's best to imagine an image as a mathematical graph of pixel values plotted along the x and y coordinates. The top right pixel in an image is your origin, which corresponds to an x value of 0 and a y value of 0.</p>
<p>Once you imagine this, any pixel in an image can be read or modified using it's coordinate in this x-y graph. For example, for a square image of size 5px x 5px, the coordinate of the center pixel is 2, 2. You may have expected it to be 3, 3, but image coordinates in this context work similar to array indexes and start from 0 for both axis.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724421916445/8d27ec1d-43f5-4cc3-b706-b9bd2efb05a4.png" alt="mathematical graph with x and y axis" class="image--center mx-auto" width="2786" height="1435" loading="lazy"></p>
<p>Approaching image processing this way also helps you address each pixel individually, making the process much simpler.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>The focus of this article is for you to understand and learn how to blend images using the Rust programming language, without going into the details of the language or it's syntax. So being comfortable writing Rust programs is required.</p>
<p>If you're not familiar with Rust, I highly encourage you to learn the basics. <a target="_blank" href="https://www.freecodecamp.org/news/rust-in-replit/">Here's an interactive Rust course that can get you started.</a></p>
<h2 id="heading-table-of-contents">Table Of Contents</h2>
<ol>
<li><p><a class="post-section-overview" href="#heading-introduction">Introduction</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-image-blending-works">How Image Blending Works</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-project-setup">Project Setup</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-read-pixel-values">How to Read Pixel Values</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-blend-functions">How to Blend Functions</a></p>
<ol>
<li><p><a class="post-section-overview" href="#heading-average-blend">Average Blend</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-multiply-blend">Multiply Blend</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-lighten-blend">Lighten Blend</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-darken-blend">Darken Blend</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-screen-blend">Screen Blend</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-addition-blend">Addition Blend</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-subtraction-blend">Subtraction Blend</a></p>
</li>
</ol>
</li>
<li><p><a class="post-section-overview" href="#heading-how-to-apply-blend-functions-to-images">How to Apply Blend Functions To Images</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-putting-it-all-together">Putting It All Together</a></p>
</li>
<li><p><a class="post-section-overview" href="#heading-glossary">Glossary</a></p>
</li>
</ol>
<h2 id="heading-introduction">Introduction</h2>
<p>Image blending refers to the technique of merging pixels from multiple images to create a single output image that is derived from all of its inputs. Depending on which blending operation is used, the image output can vary widely given the same inputs.</p>
<p>This technique serves as the basis for many complex image processing tools, some of which you may already be familiar with. Things such as removing moving people from images if you have multiple images, merging images of the night sky to create star trails, and merging multiple noise-heavy images to create a noise reduced image are all examples of this technique at play.</p>
<p>To achieve the blending of images in this tutorial, we will make use of "pixel math", which while not being a truly standard term, refers to the technique of performing mathematical operations on a pixel or set of pixels to generate an output pixel.</p>
<p>For example, to blend two images using the "average" blend mode, you will perform the mathematical average operation on all input pixels at a given location, to generate the output at the same location.</p>
<p>Pixel math is not limited to point operations, which are basically operations performed during image processing that generate a given output pixel based on input pixel from single or multiple images from the same location in the x-y coordinate system.</p>
<p>In my experience so far, the entirety of image processing field is 99% mathematics and 1% black magic. Mathematical operations on pixels and it's surrounding pixels is the basis of image manipulation techniques such as compression, resizing, blurring and sharpening, noise reduction, and so on.</p>
<h2 id="heading-how-image-blending-works"><strong>How Image Blending Works</strong></h2>
<p>The technique is technically simple to implement. Let's take the example of a simple average blend. Here's how it works:</p>
<ol>
<li><p>Read the pixel data of both images into memory, usually into an array for each image.</p>
<ul>
<li>The array is usually 2 dimensional. Each entry in array is another array for color images, the secondary array holds the 3 pixel values corresponding to Red, Green, and Blue color channels.</li>
</ul>
</li>
<li><p>For each pixel location:</p>
<ol>
<li><p>For each channel:<br> a. Take the value of the channel from the 2nd image, let's consider it <code>y</code>.<br> b. Perform the averaging operation <code>x/2 + y/2</code>.<br> c. Save the output value of this operation as the value of the output channel</p>
</li>
<li><p>Save the result of previous operation as the value of the output pixel.</p>
</li>
</ol>
</li>
<li><p>Construct the output image with the same dimensions from the computed data.</p>
</li>
</ol>
<p>You'll notice that pixel math is performed on a per-channel basis. This is always true for the blend modes we cover in this tutorial, but many techniques involve applying blends between the channels themselves and many times within the same image.</p>
<h2 id="heading-project-setup"><strong>Project Setup</strong></h2>
<p>Let's get started by setting up a project that gives us a good baseline to work with.</p>
<pre><code class="lang-bash">cargo new --bin image-blender
<span class="hljs-built_in">cd</span> image-blender
</code></pre>
<p>You will also need a single dependency to help you perform these operations:</p>
<pre><code class="lang-bash">cargo add image
</code></pre>
<p><code>image</code> is a Rust library we'll use to work with images of all of the standard formats and encodings. It also helps us convert between various formats, and provides easy access to pixel data as buffers.</p>
<p>For more information on the <code>image</code> crate, you can refer to the <a target="_blank" href="https://docs.rs/image/">official documentation</a>.</p>
<p>To follow along, you can use any two images, the only requirement being that they should be of the same size and in the same format. You can also find the images used in this tutorial, along with complete code, <a target="_blank" href="https://github.com/anshulsanghi-blog/blend-images">in the GitHub repository here</a>.</p>
<h2 id="heading-how-to-read-pixel-values"><strong>How to Read Pixel Values</strong></h2>
<p>The first step is to load the images and read their pixel values into a data structure that facilitates our operation. For this tutorial, we're going to use a <code>Vec</code> of arrays (<code>Vec&lt;[u8; 3]&gt;</code>). Each entry in the outer <code>Vec</code> represents a pixel, and the channel-wise values of each pixel are stored in <code>[u8; 3]</code> array.</p>
<p>Let's start by creating a new file to hold this code called <strong>io.rs</strong>.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/io.rs</span>

<span class="hljs-keyword">use</span> image::GenericImageView;

<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SourceData</span></span> {
    <span class="hljs-keyword">pub</span> width: <span class="hljs-built_in">usize</span>,
    <span class="hljs-keyword">pub</span> height: <span class="hljs-built_in">usize</span>,
    <span class="hljs-keyword">pub</span> image1: <span class="hljs-built_in">Vec</span>&lt;[<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>]&gt;,
    <span class="hljs-keyword">pub</span> image2: <span class="hljs-built_in">Vec</span>&lt;[<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>]&gt;,
}

<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">read_pixel_data</span></span>(image1_path: <span class="hljs-built_in">String</span>, image2_path: <span class="hljs-built_in">String</span>) -&gt; SourceData {
    <span class="hljs-comment">// Open the images</span>
    <span class="hljs-keyword">let</span> image1 = image::open(image1_path).unwrap();
    <span class="hljs-keyword">let</span> image2 = image::open(image2_path).unwrap();

    <span class="hljs-comment">// Compute image dimensions</span>
    <span class="hljs-keyword">let</span> (width, height) = image1.dimensions();
    <span class="hljs-keyword">let</span> (width, height) = (width <span class="hljs-keyword">as</span> <span class="hljs-built_in">usize</span>, height <span class="hljs-keyword">as</span> <span class="hljs-built_in">usize</span>);

    <span class="hljs-comment">// Create arrays to hold input pixel data</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> image1_data: <span class="hljs-built_in">Vec</span>&lt;[<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>]&gt; = <span class="hljs-built_in">vec!</span>[[<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>]; width * height];
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> image2_data: <span class="hljs-built_in">Vec</span>&lt;[<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>]&gt; = <span class="hljs-built_in">vec!</span>[[<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>]; width * height];

    <span class="hljs-comment">// Iterate over all pixels in the input image, along with their positions in x &amp; y</span>
    <span class="hljs-comment">// coordinates.</span>
    <span class="hljs-keyword">for</span> (x, y, pixel) <span class="hljs-keyword">in</span> image1.to_rgb8().enumerate_pixels() {
        <span class="hljs-comment">// Compute the raw values for each channel in the RGB pixel.</span>
        <span class="hljs-keyword">let</span> [r, g, b] = pixel.<span class="hljs-number">0</span>;

        <span class="hljs-comment">// Compute linear index based on 2D index. This is basically computing index in</span>
        <span class="hljs-comment">// 1D array based on the row and column index of the pixel in the 2D image.</span>
        <span class="hljs-keyword">let</span> index = (y * (width <span class="hljs-keyword">as</span> <span class="hljs-built_in">u32</span>) + x) <span class="hljs-keyword">as</span> <span class="hljs-built_in">usize</span>;

        <span class="hljs-comment">// Save the channel-wise values in the correct index in data arrays.</span>
        image1_data[index] = [r, g, b];
    }

    <span class="hljs-comment">// Iterate over all pixels in the input image, along with their positions in x &amp; y</span>
    <span class="hljs-comment">// coordinates.</span>
    <span class="hljs-keyword">for</span> (x, y, pixel) <span class="hljs-keyword">in</span> image2.to_rgb8().enumerate_pixels() {
        <span class="hljs-comment">// Compute the raw values for each channel in the RGB pixel.</span>
        <span class="hljs-keyword">let</span> [r, g, b] = pixel.<span class="hljs-number">0</span>;

        <span class="hljs-comment">// Compute linear index based on 2D index. This is basically computing index in</span>
        <span class="hljs-comment">// 1D array based on the row and column index of the pixel in the 2D image.</span>
        <span class="hljs-keyword">let</span> index = (y * (width <span class="hljs-keyword">as</span> <span class="hljs-built_in">u32</span>) + x) <span class="hljs-keyword">as</span> <span class="hljs-built_in">usize</span>;

        <span class="hljs-comment">// Save the channel-wise values in the correct index in data arrays.</span>
        image2_data[index] = [r, g, b];
    }

    SourceData {
        width,
        height,
        image1: image1_data,
        image2: image2_data,
    }
}
</code></pre>
<h2 id="heading-how-to-blend-functions">How to Blend Functions</h2>
<p>The next step is to implement the blending functions, which are pure functions that take two pixel values as input and return the output value. This is implemented through the <code>BlendOperation</code> trait defined below. Let's create a new file to host all the operations called <strong>operations.rs</strong>.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/operations.rs</span>

<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">BlendOperation</span></span> {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">perform_operation</span></span>(&amp;<span class="hljs-keyword">self</span>, pixel1: [<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>], pixel2: [<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>]) -&gt; [<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>];
}
</code></pre>
<p>Next, we need to implement this trait for all of the blending methods we want to support.</p>
<p>For showcasing the result of each of the blending modes, the following two input images are blended together</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724236939605/77d32c76-abf6-4d24-bba7-df40729863b8.jpeg" alt="Source image 1: Fireflies in a dark forest area" class="image--center mx-auto" width="3000" height="1996" loading="lazy"></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724428339241/3cc70fd2-f6da-4704-8606-97c094a2ff35.jpeg" alt="Source image 2: Fireflies in a bright forest area" class="image--center mx-auto" width="3000" height="1996" loading="lazy"></p>
<h3 id="heading-average-blend">Average Blend</h3>
<p>An average blend involves channel-wise averaging the input pixel values to get the output pixel.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/operations.rs</span>

<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">AverageBlend</span></span>;

<span class="hljs-keyword">impl</span> BlendOperation <span class="hljs-keyword">for</span> AverageBlend {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">perform_operation</span></span>(&amp;<span class="hljs-keyword">self</span>, pixel1: [<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>], pixel2: [<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>]) -&gt; [<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>] {
        [
            pixel1[<span class="hljs-number">0</span>] / <span class="hljs-number">2</span> + pixel2[<span class="hljs-number">0</span>] / <span class="hljs-number">2</span>,
            pixel1[<span class="hljs-number">1</span>] / <span class="hljs-number">2</span> + pixel2[<span class="hljs-number">1</span>] / <span class="hljs-number">2</span>,
            pixel1[<span class="hljs-number">2</span>] / <span class="hljs-number">2</span> + pixel2[<span class="hljs-number">2</span>] / <span class="hljs-number">2</span>,
        ]
    }
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724236691772/291f14f4-2019-4771-8cd2-b9f9b3cf3f86.jpeg" alt="Result of average blending source images" class="image--center mx-auto" width="3000" height="1996" loading="lazy"></p>
<h3 id="heading-multiply-blend">Multiply Blend</h3>
<p>A multiply blend involves channel-wise multiplication of input pixel values after they've been normalized<a class="post-section-overview" href="#heading-glossary">[¹]</a> to get the output pixel. The output pixel is then rescaled back to the original range by multiplying with 255.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/operations.rs</span>

<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">MultiplyBlend</span></span>;

<span class="hljs-keyword">impl</span> BlendOperation <span class="hljs-keyword">for</span> MultiplyBlend {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">perform_operation</span></span>(&amp;<span class="hljs-keyword">self</span>, pixel1: [<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>], pixel2: [<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>]) -&gt; [<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>] {
        [
            ((pixel1[<span class="hljs-number">0</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">f32</span> / <span class="hljs-number">255</span>. * pixel2[<span class="hljs-number">0</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">f32</span> / <span class="hljs-number">255</span>.) * <span class="hljs-number">255</span>.) <span class="hljs-keyword">as</span> <span class="hljs-built_in">u8</span>,
            ((pixel1[<span class="hljs-number">1</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">f32</span> / <span class="hljs-number">255</span>. * pixel2[<span class="hljs-number">1</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">f32</span> / <span class="hljs-number">255</span>.) * <span class="hljs-number">255</span>.) <span class="hljs-keyword">as</span> <span class="hljs-built_in">u8</span>,
            ((pixel1[<span class="hljs-number">2</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">f32</span> / <span class="hljs-number">255</span>. * pixel2[<span class="hljs-number">2</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">f32</span> / <span class="hljs-number">255</span>.) * <span class="hljs-number">255</span>.) <span class="hljs-keyword">as</span> <span class="hljs-built_in">u8</span>,
        ]
    }
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724236703622/9aff3ffd-9a63-4b76-9675-d7db4ccee89b.jpeg" alt="Result of multiply blending source images" class="image--center mx-auto" width="3000" height="1996" loading="lazy"></p>
<h3 id="heading-lighten-blend">Lighten Blend</h3>
<p>Lighten blend involves channel-wise comparison of input pixel values, selecting the pixel with higher value (intensity) as the output pixel.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/operations.rs</span>

<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">LightenBlend</span></span>;

<span class="hljs-keyword">impl</span> BlendOperation <span class="hljs-keyword">for</span> LightenBlend {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">perform_operation</span></span>(&amp;<span class="hljs-keyword">self</span>, pixel1: [<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>], pixel2: [<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>]) -&gt; [<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>] {
        [
            pixel1[<span class="hljs-number">0</span>].max(pixel2[<span class="hljs-number">0</span>]),
            pixel1[<span class="hljs-number">1</span>].max(pixel2[<span class="hljs-number">1</span>]),
            pixel1[<span class="hljs-number">2</span>].max(pixel2[<span class="hljs-number">2</span>]),
        ]
    }
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724236726111/5d1607fb-2740-46b8-906d-1ffb482a0561.jpeg" alt="Result of lighten blending source images" class="image--center mx-auto" width="3000" height="1996" loading="lazy"></p>
<h3 id="heading-darken-blend">Darken Blend</h3>
<p>Darken blend is the opposite operation of lighten blend. It involves channel-wise comparison of input pixel values, selecting the pixel with least value (intensity) as the output pixel.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/operations.rs</span>

<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">DarkenBlend</span></span>;

<span class="hljs-keyword">impl</span> BlendOperation <span class="hljs-keyword">for</span> DarkenBlend {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">perform_operation</span></span>(&amp;<span class="hljs-keyword">self</span>, pixel1: [<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>], pixel2: [<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>]) -&gt; [<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>] {
        [
            pixel1[<span class="hljs-number">0</span>].min(pixel2[<span class="hljs-number">0</span>]),
            pixel1[<span class="hljs-number">1</span>].min(pixel2[<span class="hljs-number">1</span>]),
            pixel1[<span class="hljs-number">2</span>].min(pixel2[<span class="hljs-number">2</span>]),
        ]
    }
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724236746972/18307fa1-1a77-4d39-b233-a7a6d87233d0.jpeg" alt="Result of darken blending source images" class="image--center mx-auto" width="3000" height="1996" loading="lazy"></p>
<h3 id="heading-screen-blend">Screen Blend</h3>
<p>Screen blend refers to multiplying the inverse of two images, and then inverting the result. In our implementation, the pixels first need to be normalized<a class="post-section-overview" href="#heading-glossary">[¹]</a>. The normalized<a class="post-section-overview" href="#heading-glossary">[¹]</a> values are then inverted by subtracting them from 1, then they're multiplied and inverted again.</p>
<p>Finally, the output is multiplied by 255 to de-normalize the output pixel value.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/operations.rs</span>

<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ScreenBlend</span></span>;

<span class="hljs-keyword">impl</span> BlendOperation <span class="hljs-keyword">for</span> ScreenBlend {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">perform_operation</span></span>(&amp;<span class="hljs-keyword">self</span>, pixel1: [<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>], pixel2: [<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>]) -&gt; [<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>] {
        [
            ((<span class="hljs-number">1</span>. - ((<span class="hljs-number">1</span>. - (pixel1[<span class="hljs-number">0</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">f32</span> / <span class="hljs-number">255</span>.)) * (<span class="hljs-number">1</span>. - (pixel2[<span class="hljs-number">0</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">f32</span> / <span class="hljs-number">255</span>.)))) * <span class="hljs-built_in">u8</span>::MAX <span class="hljs-keyword">as</span> <span class="hljs-built_in">f32</span>) <span class="hljs-keyword">as</span> <span class="hljs-built_in">u8</span>,
            ((<span class="hljs-number">1</span>. - ((<span class="hljs-number">1</span>. - (pixel1[<span class="hljs-number">1</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">f32</span> / <span class="hljs-number">255</span>.)) * (<span class="hljs-number">1</span>. - (pixel2[<span class="hljs-number">1</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">f32</span> / <span class="hljs-number">255</span>.)))) * <span class="hljs-built_in">u8</span>::MAX <span class="hljs-keyword">as</span> <span class="hljs-built_in">f32</span>) <span class="hljs-keyword">as</span> <span class="hljs-built_in">u8</span>,
            ((<span class="hljs-number">1</span>. - ((<span class="hljs-number">1</span>. - (pixel1[<span class="hljs-number">2</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">f32</span> / <span class="hljs-number">255</span>.)) * (<span class="hljs-number">1</span>. - (pixel2[<span class="hljs-number">2</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">f32</span> / <span class="hljs-number">255</span>.)))) * <span class="hljs-built_in">u8</span>::MAX <span class="hljs-keyword">as</span> <span class="hljs-built_in">f32</span>) <span class="hljs-keyword">as</span> <span class="hljs-built_in">u8</span>,
        ]
    }
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724236758380/fd531b6e-729c-4db4-987e-f503478ff950.jpeg" alt="Result of screen blending source images" class="image--center mx-auto" width="3000" height="1996" loading="lazy"></p>
<h3 id="heading-addition-blend">Addition Blend</h3>
<p>Addition blend involves adding the input values and then clamping the result to the maximum range of the color depth we're targeting. In this case, that would be 0-255 as we're targeting 8-bit color depth.</p>
<p>We also have to convert the values to u16 in order to avoid loss of value due to overflow. We can also use normalized<a class="post-section-overview" href="#heading-glossary">[¹]</a> values here to achieve the same result.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/operations.rs</span>

<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">AdditionBlend</span></span>;

<span class="hljs-keyword">impl</span> BlendOperation <span class="hljs-keyword">for</span> AdditionBlend {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">perform_operation</span></span>(&amp;<span class="hljs-keyword">self</span>, pixel1: [<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>], pixel2: [<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>]) -&gt; [<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>] {
        [
            (pixel1[<span class="hljs-number">0</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">u16</span> + pixel2[<span class="hljs-number">0</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">u16</span>).clamp(<span class="hljs-number">0</span>, <span class="hljs-built_in">u8</span>::MAX <span class="hljs-keyword">as</span> <span class="hljs-built_in">u16</span>) <span class="hljs-keyword">as</span> <span class="hljs-built_in">u8</span>,
            (pixel1[<span class="hljs-number">1</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">u16</span> + pixel2[<span class="hljs-number">1</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">u16</span>).clamp(<span class="hljs-number">0</span>, <span class="hljs-built_in">u8</span>::MAX <span class="hljs-keyword">as</span> <span class="hljs-built_in">u16</span>) <span class="hljs-keyword">as</span> <span class="hljs-built_in">u8</span>,
            (pixel1[<span class="hljs-number">2</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">u16</span> + pixel2[<span class="hljs-number">2</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">u16</span>).clamp(<span class="hljs-number">0</span>, <span class="hljs-built_in">u8</span>::MAX <span class="hljs-keyword">as</span> <span class="hljs-built_in">u16</span>) <span class="hljs-keyword">as</span> <span class="hljs-built_in">u8</span>,
        ]
    }
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724236766684/05f01177-024d-4196-a9fa-5274bb56a0f4.jpeg" alt="Result of addition blending source images" class="image--center mx-auto" width="3000" height="1996" loading="lazy"></p>
<h3 id="heading-subtraction-blend">Subtraction Blend</h3>
<p>Addition blend involves subtracting the input values and then clamping the result to the maximum range of the color depth we're targeting. In this case, that would be 0-255 as we're targeting 8-bit color depth.</p>
<p>We also convert the values to i16 in order to avoid loss of value due to overflow and lack of sign. We can also use normalized<a class="post-section-overview" href="#heading-glossary">[¹]</a> values here to achieve the same result.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/operations.rs</span>

<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">SubtractionBlend</span></span>;

<span class="hljs-keyword">impl</span> BlendOperation <span class="hljs-keyword">for</span> SubtractionBlend {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">perform_operation</span></span>(&amp;<span class="hljs-keyword">self</span>, pixel1: [<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>], pixel2: [<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>]) -&gt; [<span class="hljs-built_in">u8</span>; <span class="hljs-number">3</span>] {
        [
            (pixel1[<span class="hljs-number">0</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">i16</span> - pixel2[<span class="hljs-number">0</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">i16</span>).clamp(<span class="hljs-number">0</span>, <span class="hljs-built_in">u8</span>::MAX <span class="hljs-keyword">as</span> <span class="hljs-built_in">i16</span>) <span class="hljs-keyword">as</span> <span class="hljs-built_in">u8</span>,
            (pixel1[<span class="hljs-number">1</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">i16</span> - pixel2[<span class="hljs-number">1</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">i16</span>).clamp(<span class="hljs-number">0</span>, <span class="hljs-built_in">u8</span>::MAX <span class="hljs-keyword">as</span> <span class="hljs-built_in">i16</span>) <span class="hljs-keyword">as</span> <span class="hljs-built_in">u8</span>,
            (pixel1[<span class="hljs-number">2</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">i16</span> - pixel2[<span class="hljs-number">2</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">i16</span>).clamp(<span class="hljs-number">0</span>, <span class="hljs-built_in">u8</span>::MAX <span class="hljs-keyword">as</span> <span class="hljs-built_in">i16</span>) <span class="hljs-keyword">as</span> <span class="hljs-built_in">u8</span>,
        ]
    }
}
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1724236775603/507ba176-579d-494f-bb56-25a27ed2317f.jpeg" alt="Result of subtraction blending source images" class="image--center mx-auto" width="3000" height="1996" loading="lazy"></p>
<h2 id="heading-how-to-apply-blend-functions-to-images">How to Apply Blend Functions To Images</h2>
<p>The final step is to actually use the blending operations we created previously and apply them to pairs of images.</p>
<p>To achieve this, we need a function that can take the <code>SourceData</code> type we defined previously as input, along with a blending operation as the arguments, and gives us the final output buffer. Let's start by creating a new file for it called <strong>blend.rs</strong>.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/blend.rs</span>

<span class="hljs-keyword">use</span> image::{ImageBuffer, Rgb};
<span class="hljs-keyword">use</span> crate::{operations::BlendOperation, SourceData};

<span class="hljs-keyword">impl</span> SourceData {
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">blend_images</span></span>(&amp;<span class="hljs-keyword">self</span>, operation: <span class="hljs-keyword">impl</span> BlendOperation)  -&gt; ImageBuffer&lt;Rgb&lt;<span class="hljs-built_in">u8</span>&gt;, <span class="hljs-built_in">Vec</span>&lt;<span class="hljs-built_in">u8</span>&gt;&gt; {
        <span class="hljs-keyword">let</span> SourceData {
            width,
            height,
            image1,
            image2,
        } = <span class="hljs-keyword">self</span>;

        <span class="hljs-comment">// Create a new buffer that has the same size as input images, which will serve as our output data</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> buffer = ImageBuffer::new(*width <span class="hljs-keyword">as</span> <span class="hljs-built_in">u32</span>, *height <span class="hljs-keyword">as</span> <span class="hljs-built_in">u32</span>);

        <span class="hljs-comment">// Iterate over all pixels in the output buffer, along with their coordinates</span>
        <span class="hljs-keyword">for</span> (x, y, output_pixel) <span class="hljs-keyword">in</span> buffer.enumerate_pixels_mut() {
            <span class="hljs-comment">// Compute linear index form x &amp; y coordinates. In other words, you have the</span>
            <span class="hljs-comment">// row and column indexes here, and you want to compute the array index based</span>
            <span class="hljs-comment">// on these two positions.</span>
            <span class="hljs-keyword">let</span> index = (y * *width <span class="hljs-keyword">as</span> <span class="hljs-built_in">u32</span> + x) <span class="hljs-keyword">as</span> <span class="hljs-built_in">usize</span>;

            <span class="hljs-comment">// Store pixel values in the given position into variables</span>
            <span class="hljs-keyword">let</span> pixel1 = image1[index];
            <span class="hljs-keyword">let</span> pixel2 = image2[index];

            <span class="hljs-comment">// Compute the blended pixel and convert it into the `Rgb` type, which is then</span>
            <span class="hljs-comment">// assigned to the output pixel in the buffer.</span>
            *output_pixel = Rgb::from(operation.perform_operation(pixel1, pixel2));
        }

        buffer
    }
}
</code></pre>
<h3 id="heading-putting-it-all-together">Putting It All Together</h3>
<p>It's now time to make use of all the new things you've learnt so far, and put them together in <strong>main.rs</strong> file.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/main.rs</span>

<span class="hljs-keyword">mod</span> blend;
<span class="hljs-keyword">mod</span> io;
<span class="hljs-keyword">mod</span> operations;

<span class="hljs-keyword">use</span> io::*;
<span class="hljs-keyword">use</span> operations::{
    AdditionBlend, AverageBlend, DarkenBlend, LightenBlend, MultiplyBlend, ScreenBlend,
    SubtractionBlend,
};

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> source_data = read_pixel_data(<span class="hljs-string">"image1.jpg"</span>.to_string(), <span class="hljs-string">"image2.jpg"</span>.to_string());

    <span class="hljs-keyword">let</span> output_buffer = source_data.blend_images(AdditionBlend);
    output_buffer.save(<span class="hljs-string">"addition.jpg"</span>).unwrap();

    <span class="hljs-keyword">let</span> output_buffer = source_data.blend_images(AverageBlend);
    output_buffer.save(<span class="hljs-string">"average.jpg"</span>).unwrap();

    <span class="hljs-keyword">let</span> output_buffer = source_data.blend_images(DarkenBlend);
    output_buffer.save(<span class="hljs-string">"darken.jpg"</span>).unwrap();

    <span class="hljs-keyword">let</span> output_buffer = source_data.blend_images(LightenBlend);
    output_buffer.save(<span class="hljs-string">"lighten.jpg"</span>).unwrap();

    <span class="hljs-keyword">let</span> output_buffer = source_data.blend_images(MultiplyBlend);
    output_buffer.save(<span class="hljs-string">"multiply.jpg"</span>).unwrap();

    <span class="hljs-keyword">let</span> output_buffer = source_data.blend_images(ScreenBlend);
    output_buffer.save(<span class="hljs-string">"screen.jpg"</span>).unwrap();

    <span class="hljs-keyword">let</span> output_buffer = source_data.blend_images(SubtractionBlend);
    output_buffer.save(<span class="hljs-string">"subtraction.jpg"</span>).unwrap();
}
</code></pre>
<p>You can now run the program using the following command, and you should have all the images generated and saved in the project folder:</p>
<pre><code class="lang-bash">cargo run --release
</code></pre>
<p>As you might have guessed already, this implementation only works for 8-bit RGB images. This code, however, can be extended very easily to support the other color formats such as 8-bit Luma (Monochrome), 16-bit RGB (Many RAW camera images), and so on.</p>
<p>I highly encourage you to try that out. You can also reach out to me for help with anything in this tutorial or with extending the code in this tutorial. I'd be happy to answer all your queries. Email is the best way to reach me, you can email me at <a target="_blank" href="mailto:anshul@anshulsanghi.tech">anshul@anshulsanghi.tech</a>.</p>
<h3 id="heading-glossary">Glossary</h3>
<p>Normalization refers to the process of rescaling the pixel values so that the values are in floating point format and are in the range of 0-1. For example, for an 8 bit image, the color black is represented by 0 (0 in de-normalized value) and the color white is represented by 1 (255 in de-normalized value). Intermediary decimal values between 0 &amp; 1 represent different intensities of the pixel between black and white. Normalization is done for many different reasons such as:</p>
<ul>
<li><p>Preventing overflows during calculations.</p>
</li>
<li><p>Re-scaling images to the same range irrespective of their individual color depth.</p>
</li>
<li><p>Expanding possible dynamic range of the image.</p>
</li>
</ul>
<h3 id="heading-enjoying-my-work"><strong>Enjoying my work?</strong></h3>
<p>Consider buying me a coffee to support my work!</p>
<p><a target="_blank" href="https://www.buymeacoffee.com/anshulsanghi"><img src="https://img.buymeacoffee.com/button-api/?text=Buy%20me%20a%20coffee&amp;emoji=%E2%98%95&amp;slug=anshulsanghi&amp;button_colour=FFDD00&amp;font_colour=000000&amp;font_family=Cookie&amp;outline_colour=000000&amp;coffee_colour=ffffff" alt="?text=Buy%20me%20a%20coffee&amp;emoji=%E2%98%95&amp;slug=anshulsanghi&amp;button_colour=FFDD00&amp;font_colour=000000&amp;font_family=Cookie&amp;outline_colour=000000&amp;coffee_colour=ffffff" width="235" height="50" loading="lazy"></a></p>
<p>Till next time, happy coding and wishing you clear skies!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Rust Tutorial – Learn Advanced Iterators & Pattern Matching by Building a JSON Parser ]]>
                </title>
                <description>
                    <![CDATA[ Iterators and match patterns are two of the most used language features in Rust. If you have written any real world application, big or small, chances are that you've already used these, whether knowingly or unknowingly. In this tutorial, I aim to he... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/rust-tutorial-build-a-json-parser/</link>
                <guid isPermaLink="false">66bb579d7b4b3dfb68522740</guid>
                
                    <category>
                        <![CDATA[ Rust ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Anshul Sanghi ]]>
                </dc:creator>
                <pubDate>Wed, 29 May 2024 10:45:15 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/05/JSON-Parser-Cover.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Iterators and match patterns are two of the most used language features in Rust. If you have written any real world application, big or small, chances are that you've already used these, whether knowingly or unknowingly.</p>
<p>In this tutorial, I aim to help you understand how they actually work, the many ways they're usually used, and how powerful they are by writing a JSON parser that heavily uses these features.</p>
<h2 id="heading-disclaimer">Disclaimer</h2>
<p>The goal of this tutorial is to create a real-world library that extensively uses match patterns and iterators. The goal is not to write a performant or fully-compliant JSON parser.</p>
<p>If you're very familiar with JSON, you will notice many things that are missing in this code, the biggest one being error handling when invalid tokens are encountered, and giving feedback to the user or helpful suggestions on what's wrong with the JSON.</p>
<p>This program also doesn't handle escape characters and sequences within string literals, for example. The code, for the most part, assumes that you have a valid JSON.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>Although this tutorial can be consumed by Rust programmers of any experience, previous experience or understanding of basic iterators and match patterns in Rust is helpful.</p>
<p>It is also assumed that you're familiar with the most basic Rust concepts such as <code>traits</code>, <code>structs</code>, <code>enums</code>, <code>for</code> loops, <code>impl</code> blocks, and so on. The tutorial does introduce you to <code>iterator</code> and <code>match</code>, so you don't need to be familiar with those to benefit from this tutorial.</p>
<h2 id="heading-table-of-contents">Table Of Contents</h2>
<ol>
<li><a class="post-section-overview" href="#heading-what-are-iterators-in-rust">What are Iterators in Rust?</a><ol>
<li><a class="post-section-overview" href="#heading-how-to-implement-iterators-in-rust">How to implement iterators in Rust</a></li>
<li><a class="post-section-overview" href="#heading-what-are-peekable-iterators-in-rust">What are peekable iterators in Rust?</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#heading-what-is-the-match-statement-in-rust">What is The Match Statement in Rust?</a><ol>
<li><a class="post-section-overview" href="#heading-how-to-use-iterators-in-match-statements-in-rust">How to use iterators in match statements in Rust</a></li>
<li><a class="post-section-overview" href="#heading-what-are-match-guards-in-rust">What are match guards in Rust?</a></li>
<li><a class="post-section-overview" href="#heading-what-is-binding-in-rust">What is binding in Rust?</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#heading-how-to-build-a-json-parser-stage-1-reader">How to Build a JSON Parser – Stage 1: Reader</a><ol>
<li><a class="post-section-overview" href="#heading-what-is-the-utf-8-byte-encoding">What is the UTF-8 byte encoding?</a></li>
<li><a class="post-section-overview" href="#heading-how-to-read-the-data">How to read the data</a></li>
<li><a class="post-section-overview" href="#heading-how-to-implement-the-iterator-for-jsonreader">How to implement the iterator for JsonReader</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#heading-how-to-build-a-json-parser-stage-2-prepare-intermediate-data-types">How to Build a JSON Parser – Stage 2: Prepare Intermediate Data Types</a><ol>
<li><a class="post-section-overview" href="#heading-the-value-type">The value type</a></li>
<li><a class="post-section-overview" href="#heading-how-to-add-helpful-conversion-methods">How to add helpful conversion methods</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#heading-how-to-build-a-json-parser-stage-3-tokenization">How to Build a JSON Parser – Stage 3: Tokenization</a><ol>
<li><a class="post-section-overview" href="#heading-how-to-define-expected-valid-tokens">How to define expected valid tokens</a></li>
<li><a class="post-section-overview" href="#heading-how-to-implement-the-tokenizer-struct">How to implement the tokenizer struct</a></li>
<li><a class="post-section-overview" href="#heading-how-to-tokenize-an-iterator-of-characters">How to tokenize an iterator of characters</a></li>
<li><a class="post-section-overview" href="#heading-how-to-parse-string-tokens">How to parse string tokens</a></li>
<li><a class="post-section-overview" href="#heading-how-to-parse-number-tokens">How to parse number tokens</a></li>
<li><a class="post-section-overview" href="#heading-how-to-parse-boolean-tokens">How to parse boolean tokens</a></li>
<li><a class="post-section-overview" href="#heading-how-to-parse-null-literal">How to parse Null Literal</a></li>
<li><a class="post-section-overview" href="#heading-how-to-parse-delimiters">How to parse delimiters</a></li>
<li><a class="post-section-overview" href="#heading-how-to-parse-a-terminating-character">How to parse a terminating character</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#heading-how-to-build-a-json-parser-stage-4-from-tokens-to-value">How to Build a JSON Parser – Stage 4: From Tokens To Value</a><ol>
<li><a class="post-section-overview" href="#heading-how-to-parse-primitives">How to parse primitives</a></li>
<li><a class="post-section-overview" href="#heading-how-to-parse-arrays">How to parse arrays</a></li>
<li><a class="post-section-overview" href="#heading-how-to-parse-objects">How to parse objects</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#heading-how-to-use-the-json-parser">How to Use the JSON parser</a></li>
<li><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></li>
</ol>
<h2 id="heading-what-are-iterators-in-rust">What are Iterators in Rust?</h2>
<p>Iterators are not a new concept, neither are they specific to Rust. It's both a pattern that is also implemented as an object in most programming languages for working with lists (such as arrays or vectors) or collections (such as HashMaps) that allows you to traverse through these data types and act on individual entries in them.</p>
<p>In Rust, iterators are a very powerful feature. The official Rust book describes it as:</p>
<blockquote>
<p>The iterator pattern allows you to perform some task on a sequence of items in turn. An iterator is responsible for the logic of iterating over each item and determining when the sequence has finished. When you use iterators, you don’t have to reimplement that logic yourself.</p>
<p>In Rust, iterators are <em>lazy</em>, meaning they have no effect until you call methods that consume the iterator to use it up.</p>
</blockquote>
<p>An iterator is an object that facilitates sequential access to the elements of a collection, like an array or a vector, without exposing the implementation details.</p>
<h3 id="heading-how-to-implement-iterators-in-rust">How to implement iterators in Rust</h3>
<p>Iterators are implemented in Rust using a collection of traits, the most basic of which is the <code>Iterator</code> trait. It is implemented for all collections in the standard library, and can be implemented for custom types as well. </p>
<blockquote>
<p>It requires the implementation of a single method: <code>next()</code>. This method returns an <code>Option&lt;T&gt;</code>, where <code>T</code> is the type of element the iterator is for. When <code>next()</code> is called (the call is implicit in most cases and you generally use higher level methods), the iterator produces <code>Some(value)</code> for the next element in the sequence or <code>None</code> when the iteration is complete.  In most cases, whether the value is <code>Some</code> or <code>None</code> is also implicit.</p>
</blockquote>
<p>For example, anything that implements the <code>Iterator</code> trait, can be used with a for loop directly, which implicitly handles both calling the <code>next</code> method as well as handling whether the value is <code>Some</code> or <code>None</code>. A <code>None</code> value triggers the loop to end. This is true for the built-in types such as arrays, slices, vectors, and hash-maps as well.</p>
<p>For example, let's implement the iterator trait on a simple custom type. You need to store the current state of the iterator in the type. You can also store any additional information you need. Here, we just need to know the max number after which the iteration should end:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> std::iter::<span class="hljs-built_in">Iterator</span>;

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">CustomType</span></span> {
    current: <span class="hljs-built_in">usize</span>,
    max: <span class="hljs-built_in">usize</span>,
}

<span class="hljs-keyword">impl</span> CustomType {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">new</span></span>(max: <span class="hljs-built_in">usize</span>) -&gt; <span class="hljs-keyword">Self</span> {
        <span class="hljs-keyword">Self</span> {
            current: <span class="hljs-number">0</span>,
            max,
        }
    }
}

<span class="hljs-keyword">impl</span> <span class="hljs-built_in">Iterator</span> <span class="hljs-keyword">for</span> CustomType {
    <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Item</span></span> = <span class="hljs-built_in">usize</span>;

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">next</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">Option</span>&lt;Self::Item&gt; {
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">self</span>.current &gt;= <span class="hljs-keyword">self</span>.max {
            <span class="hljs-literal">None</span>
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">self</span>.current += <span class="hljs-number">1</span>;
            <span class="hljs-literal">Some</span>(<span class="hljs-keyword">self</span>.current)
        }
    }
}

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> custom = CustomType::new(<span class="hljs-number">10</span>);

    <span class="hljs-keyword">for</span> item <span class="hljs-keyword">in</span> custom {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Item is {item}"</span>);
    }
}
</code></pre>
<pre><code class="lang-shell"># Output

Item is 1
Item is 2
Item is 3
Item is 4
Item is 5
Item is 6
Item is 7
Item is 8
Item is 9
Item is 10
</code></pre>
<p>Rust iterators are also lazily evaluated, meaning they do not do anything unless they're used. This means that until you actually want to get the next value and do something with it, it won't even compute what the next value is.</p>
<p>This also means that if you have a chain of operations, such as a map and a filter, each item will go through the entire pipeline first, before the code processes the next item. This is unlike many other languages which support <code>map</code> and <code>filter</code> as methods, where first the entire map will be processed for all operations, and then the filter will be performed.</p>
<p>If you think about it carefully, iterators allow us to write parallel processing pipelines in a much easier way than the counterpart.</p>
<p>Since <code>Iterator</code> is just a trait, it allows for iterators to be chain-able and transformable to other iterators using various adapter methods (either the ones in standard library, or the ones that you can implement yourself).</p>
<h3 id="heading-what-are-peekable-iterators-in-rust">What are peekable iterators in Rust?</h3>
<p>Many times, you need the ability to know what the next element will be for deciding what to do, without actually modifying the iterator state for it to move to the next element. This is especially necessary when working with an iterator of tokens for parsing, as we'll do later in this tutorial.</p>
<p>This is where the <code>Peekable</code> struct comes in. You can convert any iterator into a peekable iterator by calling the <code>peekable</code> method on it.</p>
<p>Let's take the previous example and see how peekable works in action:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> std::iter::<span class="hljs-built_in">Iterator</span>;

<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">CustomType</span></span> {
    current: <span class="hljs-built_in">usize</span>,
    max: <span class="hljs-built_in">usize</span>,
}

<span class="hljs-keyword">impl</span> CustomType {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">new</span></span>(max: <span class="hljs-built_in">usize</span>) -&gt; <span class="hljs-keyword">Self</span> {
        <span class="hljs-keyword">Self</span> {
            current: <span class="hljs-number">0</span>,
            max,
        }
    }
}

<span class="hljs-keyword">impl</span> <span class="hljs-built_in">Iterator</span> <span class="hljs-keyword">for</span> CustomType {
    <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Item</span></span> = <span class="hljs-built_in">usize</span>;

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">next</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">Option</span>&lt;Self::Item&gt; {
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">self</span>.current &gt;= <span class="hljs-keyword">self</span>.max {
            <span class="hljs-literal">None</span>
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-keyword">self</span>.current += <span class="hljs-number">1</span>;
            <span class="hljs-literal">Some</span>(<span class="hljs-keyword">self</span>.current)
        }
    }
}

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> custom = CustomType::new(<span class="hljs-number">2</span>).peekable();

    <span class="hljs-keyword">let</span> first = custom.peek();
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{first:?}"</span>);

    <span class="hljs-keyword">let</span> second = custom.next();
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{second:?}"</span>);

    <span class="hljs-keyword">let</span> third = custom.next();
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{third:?}"</span>);

    <span class="hljs-keyword">let</span> fourth = custom.next();
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{fourth:?}"</span>);
}
</code></pre>
<pre><code class="lang-shell"># Output

Some(1)
Some(1)
Some(2)
None
</code></pre>
<p>I also wanted to show you how you can use iterators manually without a for loop, which is why you see all the calls to <code>next</code> method, and also that it returns <code>Option</code> instead of the value directly.</p>
<p>Also notice that the <code>first</code> and <code>second</code> variables are both <code>Some(1)</code>. This is because we called <code>peek</code> the first time which returned the first element but without modifying the state of the iterator.</p>
<h2 id="heading-what-is-the-match-statement-in-rust">What is The Match Statement in Rust?</h2>
<p>The match statement is a pattern-matching syntax in Rust that allows you to conditionally run code based on complex conditions in a concise syntax. You can think of it as a <code>switch</code> statement from other languages, but on steroids.</p>
<p>A very simple example of a match statement is:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> value = <span class="hljs-literal">true</span>;

<span class="hljs-keyword">match</span> value {
    <span class="hljs-literal">true</span> =&gt; {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Value is true"</span>)
    },
    <span class="hljs-literal">false</span> =&gt; {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Value is false"</span>)
    }
}
</code></pre>
<p>The various conditions defined above, namely <code>true</code> and <code>false</code>, are known as branches. Each branch can have a single match, multiple matches separated by the pipe <code>|</code> operator, and ranges. They also can have <code>guards</code> and <code>binding</code> for each arm. Let's see what each of these mean:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Multiple conditions per branch</span>

<span class="hljs-keyword">let</span> value = <span class="hljs-string">"some_string"</span>;

<span class="hljs-keyword">match</span> value {
    <span class="hljs-string">"some_string1"</span> | <span class="hljs-string">"some_string2"</span> | <span class="hljs-string">"some_string3"</span> =&gt; {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Bad match"</span>);
    }
    <span class="hljs-string">"some_string"</span> =&gt; {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Good match"</span>);
    }
    _ =&gt; {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"No match"</span>);
    }
}
</code></pre>
<p>Notice the <code>_</code> branch in the above example. Match statements require you to cover all possible cases. In the first example, since the value was a boolean, there were only two possible values, <code>true</code> and <code>false</code>. This is why in the first case, we  already covered all possible values.</p>
<p>In the 2nd example however, the value we're matching against is a string ( <code>&amp;str</code> to be more precise). A string can be any value. It's impossible to write a match statement that can ever cover all possible cases for this example. Good thing is, Rust has a special matcher <code>_</code> that matches any value. </p>
<p>If you're experienced with JavaScript or C (or many other languages that have the traditional <code>switch</code> syntax), <code>_</code> is equivalent to the <code>default</code> case in <code>switch</code>, but you don't need to use <code>_</code>, you can also bind it to a variable and handle it differently. We'll see how to do this shortly.</p>
<h3 id="heading-how-to-use-iterators-in-match-statements-in-rust">How to use iterators in match statements in Rust</h3>
<p>A match statement allows you to use iterators as branches. A successful match occurs if the value being matched is one of the values in the iterator. For example, say you are matching if a <code>char</code> type is a digit or not. You can write a simple iterator of characters that contains all the digit characters and use that as a branch:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> value: <span class="hljs-built_in">char</span> = <span class="hljs-string">'5'</span>;

<span class="hljs-keyword">match</span> value {
    <span class="hljs-string">'0'</span>..=<span class="hljs-string">'9'</span> =&gt; {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Character is a digit"</span>);
    }
    _ =&gt; {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Character is not a digit"</span>);
    }
}
</code></pre>
<p>The above example will print "Character is a digit". If you're not familiar with the <code>..=</code> syntax, it's a shorthand to create iterators over a range. In the example above, the iterator starts at <code>'0'</code> character and ends at <code>'9'</code> character, including all of the characters in between.</p>
<p>You can also use <code>1..5</code> to create a iterator over the range between 1 and 5 but excluding 5, so that the iterator will contain <code>1, 2, 3, 4</code>.</p>
<p>You can also use a variable that holds the iterator as the value, meaning that the iterators do not need to be created inline:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> list = <span class="hljs-built_in">vec!</span>[<span class="hljs-string">"1, 2"</span>, <span class="hljs-string">"3, 4"</span>].iter();
    <span class="hljs-keyword">let</span> value = <span class="hljs-string">"3, 4"</span>;

    <span class="hljs-keyword">match</span> value {
        list =&gt; {
            <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Matched"</span>);
        }
        _ =&gt; {
            <span class="hljs-built_in">println!</span>(<span class="hljs-string">"No matches"</span>);
        }
    }
</code></pre>
<p>Note that the example calls <code>.iter()</code> on the vec to store the iterator in <code>list</code> variable and not the vector. Match arms cannot have method calls, so it's important to convert the value to an iterator outside of the match statement.</p>
<h3 id="heading-what-are-match-guards-in-rust">What are match guards in Rust?</h3>
<p>Guards in match statements are additional conditions for a particular branch that the branch must satisfy to consider a successful match. For example, if you want to match over a range of numbers, but also match on whether they're odd or even, match guards can come in handy.</p>
<p>The syntax is also pretty intuitive. It is of the form <code>&lt;pattern&gt; if &lt;condition&gt; =&gt; {}</code>.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> value: <span class="hljs-built_in">u8</span> = <span class="hljs-number">5</span>;

<span class="hljs-keyword">match</span> value {
    <span class="hljs-number">0</span>..=<span class="hljs-number">9</span> <span class="hljs-keyword">if</span> value % <span class="hljs-number">2</span> == <span class="hljs-number">0</span> =&gt; {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Value is even"</span>);
    }
    <span class="hljs-number">0</span>..=<span class="hljs-number">9</span> <span class="hljs-keyword">if</span> value % <span class="hljs-number">2</span> == <span class="hljs-number">1</span> =&gt; {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Value is odd"</span>);
    }
    _ =&gt; {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Unexpected value"</span>);
    }
}
</code></pre>
<p>The above code will print "Value is odd".</p>
<h3 id="heading-what-is-binding-in-rust">What is binding in Rust?</h3>
<p>Binding allows you to store values in variables that can be used within the branch where the binding is present. It's basically assigning variables to certain parts of the pattern. </p>
<h4 id="heading-pattern-binding">Pattern Binding</h4>
<p>A very simple example is binding the catch-all pattern to a variable instead of ignoring its value using <code>_</code>.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> value: <span class="hljs-built_in">u8</span> = <span class="hljs-number">5</span>;

<span class="hljs-keyword">match</span> value {
    <span class="hljs-number">0</span>..=<span class="hljs-number">9</span> <span class="hljs-keyword">if</span> value % <span class="hljs-number">2</span> == <span class="hljs-number">0</span> =&gt; {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Value is even"</span>);
    }
    <span class="hljs-number">0</span>..=<span class="hljs-number">9</span> <span class="hljs-keyword">if</span> value % <span class="hljs-number">2</span> == <span class="hljs-number">1</span> =&gt; {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Value is odd"</span>);
    }
    other_value =&gt; {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Unexpected value: {other_value}"</span>);
    }
}
</code></pre>
<p>Notice that in this example, we used the variable <code>other_value</code> to bind the value of <code>value</code> in the last pattern, which is a catch-all if it doesn't match any of the previous patterns. We can then use the variable in logic for that arm. Here we just print it the console.</p>
<p>Some other examples of binding are:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> value: <span class="hljs-built_in">Option</span>&lt;<span class="hljs-built_in">i32</span>&gt; = <span class="hljs-literal">Some</span>(<span class="hljs-number">43</span>);

<span class="hljs-keyword">match</span> value {
    <span class="hljs-literal">Some</span>(matched_value) =&gt; <span class="hljs-built_in">println!</span>(<span class="hljs-string">"The value is {matched_value}"</span>),
    <span class="hljs-literal">None</span> =&gt; <span class="hljs-built_in">println!</span>(<span class="hljs-string">"There is no value"</span>)
}
</code></pre>
<p>In this example, we bound the value within the <code>Some</code> pattern for storing the inner value of the option, and use it in our logic.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">Person</span></span> {
    name: <span class="hljs-built_in">String</span>,
    age: <span class="hljs-built_in">u32</span>,
}

<span class="hljs-keyword">let</span> value: <span class="hljs-built_in">Option</span>&lt;Person&gt; = <span class="hljs-literal">Some</span>(Person {
    name: <span class="hljs-string">"Name"</span>.to_string(),
    age: <span class="hljs-number">23</span>,
});

<span class="hljs-keyword">match</span> value {
    <span class="hljs-literal">Some</span>(Person { name: person_name, age }) =&gt; {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{person_name} is {age} years old"</span>);
    },
    <span class="hljs-literal">None</span> =&gt; {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"The value is empty"</span>);
    }
}
</code></pre>
<p>We see two different types of binding in this example. The first is assigning a different name to a struct field by destructuring it ( <code>name</code> field), and the other is using the same name as the name of the field ( <code>age</code> field).</p>
<h4 id="heading-the-binding">The <code>@</code> Binding</h4>
<p>The official Rust book describes it as:</p>
<blockquote>
<p>The at operator @ lets us create a variable that holds a value at the same time as we’re testing that value for a pattern match.</p>
</blockquote>
<p>In our example of pattern matching against a range of values, or against an iterator, we can bind the matched value to a variable using this syntax to use it within that branch:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> value: <span class="hljs-built_in">u8</span> = <span class="hljs-number">5</span>;

<span class="hljs-keyword">match</span> value {
    digit @ <span class="hljs-number">0</span>..=<span class="hljs-number">9</span> =&gt; {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"The matched value is {digit}"</span>);
    }
    _ =&gt; {
        <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Unexpected value"</span>);
    }
}
</code></pre>
<p>Here we are binding the matched value from the iterator to the <code>digit</code> variable, that we then use within the branch to read the actual value.</p>
<h2 id="heading-how-to-build-a-json-parser-stage-1-reader">How to Build a JSON Parser – Stage 1: Reader</h2>
<p>Before we can parse incoming JSON data, we need to be able to read it in a way which facilitates parsing it. To be able to tokenize the incoming JSON, we need to analyse each character as they come in, and based on whether they represent a literal value, or a delimiter, (or an invalid value), decide what to do with them as well as subsequent characters.</p>
<p>This is a really good use case for using a combination of iterators and Rust's match syntax.</p>
<p>Our reader needs to hold two pieces of data. A buffered reader using which we can iterate over the input, and a <code>character_buffer</code>, which will hold the current character being decoded. </p>
<p>At this point, you may ask why we need to hold the character buffer in the reader and the reason is that JSON is UTF-8 encoded.</p>
<h3 id="heading-what-is-the-utf-8-byte-encoding">What is the UTF-8 byte encoding?</h3>
<p>A UTF-8 character can be anywhere between 1 byte to 4 bytes long. We need to be able to parse all of the valid characters because the JSON spec supports these characters. This means that JSON characters can be either of 1-byte, 2-bytes, 3-bytes or 4-bytes long.</p>
<p>For each iteration, we need to read 4 bytes at a time, decide how many characters the 4 bytes contain (for example, these 4 bytes can contain 4 1-byte characters), finish iterating over them and then move on to the reading next 4 bytes and repeating the process. To store this intermediary piece of information, we need the character buffer.</p>
<p>It is also possible that we only have part of the character in the current 4 byte. For example, if you consider 2 1-byte characters followed by 1 3-byte character like <code>23€</code>, the first 4 bytes will contain 2 valid characters and only part of the next valid character. You also need to be able to handle this, which will involve rewinding the iterator.</p>
<p>It's possible to handle this in a way where we do not need allocations, and for performance reasons it's in fact better to do so. But I will leave that to you as a reader to think about how to implement it in this case, as it is not the focus of this article.</p>
<p>I hope that it's now clear why we iterators are the best tool for the job here.</p>
<h3 id="heading-how-to-read-the-data">How to read the data</h3>
<p>We are going to support two different readers. One is directly from a buffered reader (which is most commonly created from a file), and the other is from a iterator over bytes.</p>
<p>These are going to be pretty straightforward. For reading from a file, you need to create a buffered cursor over the underlying file data:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> file = File::create(<span class="hljs-string">"dummy.json"</span>).unwrap();
<span class="hljs-keyword">let</span> reader = BufReader::new(file);
</code></pre>
<p>Let's start by implementing the JSON Reader struct and these methods on it:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/reader.rs</span>

<span class="hljs-keyword">use</span> std::collections::VecDeque;
<span class="hljs-keyword">use</span> std::io::{BufReader, Cursor, Read, Seek};
<span class="hljs-keyword">use</span> std::<span class="hljs-built_in">str</span>::from_utf8;

<span class="hljs-comment">/// A struct that handles reading input data to be parsed and</span>
<span class="hljs-comment">/// provides an iterator over said data character-by-character.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">JsonReader</span></span>&lt;T&gt;
<span class="hljs-keyword">where</span>
    T: Read + Seek,
{
    <span class="hljs-comment">/// A reference to the input data, which can be anything</span>
    <span class="hljs-comment">/// that implements [`Read`]</span>
    reader: BufReader&lt;T&gt;,

    <span class="hljs-comment">/// A character buffer that holds queue of characters to</span>
    <span class="hljs-comment">/// be used by the iterator.</span>
    <span class="hljs-comment">///</span>
    <span class="hljs-comment">/// This is necessary because UTF-8 can be 1-4 bytes long.</span>
    <span class="hljs-comment">/// Because of this, the reader always reads 4 bytes at a </span>
    <span class="hljs-comment">/// time. We then iterate over "characters", irrespective of </span>
    <span class="hljs-comment">/// whether they are 1 byte long, or 4.</span>
    <span class="hljs-comment">///</span>
    <span class="hljs-comment">/// A [`VecDeque`] is used instead of a normal vector </span>
    <span class="hljs-comment">/// because characters need to be read out from the start </span>
    <span class="hljs-comment">/// of the buffer.</span>
    character_buffer: VecDeque&lt;<span class="hljs-built_in">char</span>&gt;,
}

<span class="hljs-keyword">impl</span>&lt;T&gt; JsonReader&lt;T&gt;
<span class="hljs-keyword">where</span>
    T: Read + Seek,
{
    <span class="hljs-comment">/// Create a new [`JsonReader`] that reads from a file</span>
    <span class="hljs-comment">///</span>
    <span class="hljs-comment">/// # Examples</span>
    <span class="hljs-comment">///</span>
    <span class="hljs-comment">///</span>
</code></pre>
<p>    /// use std::fs::File;
    /// use std::io::BufReader;
    /// use json_parser::reader::JsonReader;
    ///
    /// let file = File::create("dummy.json").unwrap();
    /// let reader = BufReader::new(file);
    ///
    /// let json_reader = JsonReader::new(reader);
    /// ```
    pub fn new(reader: BufReader) -&gt; Self {
        JsonReader {
            reader,
            character_buffer: VecDeque::with_capacity(4),
        }
    }</p>
<p>    /// Create a new [<code>JsonReader</code>] that reads from a given byte stream
    ///
    /// # Examples
    ///
    /// <code>/// use std::io::{BufReader, Cursor};
    /// use json_parser::reader::JsonReader;
    ///
    /// let input_json_string = r#"{"key1":"value1","key2":"value2"}"#;
    ///
    /// let json_reader = JsonReader::&lt;Cursor&lt;&amp;'static [u8]&gt;&gt;::from_bytes(input_json_string.as_bytes());
    ///</code></p>
<p>    #[must_use]
    pub fn from_bytes(bytes: &amp;[u8]) -&gt; JsonReader&gt; {
        JsonReader {
            reader: BufReader::new(Cursor::new(bytes)),
            character_buffer: VecDeque::with_capacity(4),
        }
    }
}</p>
<pre><code>
### How to implement the iterator <span class="hljs-keyword">for</span> <span class="hljs-string">`JsonReader`</span>

Next, you are going to need to implement the <span class="hljs-string">`Iterator`</span> trait on <span class="hljs-built_in">this</span> <span class="hljs-string">`JSONReader`</span> which will facilitate parsing.

First, <span class="hljs-keyword">if</span> the character buffer isn<span class="hljs-string">'t empty already, you can return the first character in buffer from iterator:

```rust
if !self.character_buffer.is_empty() {
    return self.character_buffer.pop_front();
}</span>
</code></pre><p>If it is empty, you need to create a new buffer and read into that buffer from the reader:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> utf8_buffer = [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>];
<span class="hljs-keyword">let</span> _ = <span class="hljs-keyword">self</span>.reader.read(&amp;<span class="hljs-keyword">mut</span> utf8_buffer);
</code></pre>
<p>Here, you are creating a new array of size 4, and you'll be reading 4 bytes into it from the reader. </p>
<p>Next, you need to parse it as UTF-8. Rust provides you with a <code>from_utf8</code> function that will try to parse the given bytes as UTF-8. It returns a string containing parsed characters if it was valid. </p>
<p>It returns an error with number of invalid bytes as part of the error information, which you can use to backtrack the reader to only retain the valid characters, and try the next 4 characters from the point of failure.</p>
<p>If that didn't make too much sense, looking at the code will make things clear:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">match</span> from_utf8(&amp;utf8_buffer) {
    <span class="hljs-literal">Ok</span>(string) =&gt; {
        <span class="hljs-keyword">self</span>.character_buffer = string.chars().collect();
        <span class="hljs-keyword">self</span>.character_buffer.pop_front()
    }
    <span class="hljs-literal">Err</span>(error) =&gt; {
        <span class="hljs-comment">// Read valid bytes, and rewind the buffered reader for </span>
        <span class="hljs-comment">// the remaining bytes so that they can be read again in the</span>
        <span class="hljs-comment">// next iteration.</span>

        <span class="hljs-keyword">let</span> valid_bytes = error.valid_up_to();
        <span class="hljs-keyword">let</span> string = from_utf8(&amp;utf8_buffer[..valid_bytes]).unwrap();

        <span class="hljs-keyword">let</span> remaining_bytes = <span class="hljs-number">4</span> - valid_bytes;

        <span class="hljs-keyword">let</span> _ = <span class="hljs-keyword">self</span>.reader.seek_relative(-(remaining_bytes <span class="hljs-keyword">as</span> <span class="hljs-built_in">i64</span>));

        <span class="hljs-comment">// Collect the valid characters into character_buffer</span>
        <span class="hljs-keyword">self</span>.character_buffer = string.chars().collect();

        <span class="hljs-comment">// Return the first character from character_buffer</span>
        <span class="hljs-keyword">self</span>.character_buffer.pop_front()
    }
}
</code></pre>
<p>Here's the complete implementation of the <code>Iterator</code> trait:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/reader.rs</span>

<span class="hljs-keyword">impl</span>&lt;T&gt; <span class="hljs-built_in">Iterator</span> <span class="hljs-keyword">for</span> JsonReader&lt;T&gt;
<span class="hljs-keyword">where</span>
    T: Read + Seek,
{
    <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Item</span></span> = <span class="hljs-built_in">char</span>;

    <span class="hljs-meta">#[allow(clippy::cast_possible_wrap)]</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">next</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">Option</span>&lt;Self::Item&gt; {
        <span class="hljs-keyword">if</span> !<span class="hljs-keyword">self</span>.character_buffer.is_empty() {
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">self</span>.character_buffer.pop_front();
        }

        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> utf8_buffer = [<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>];
        <span class="hljs-keyword">let</span> _ = <span class="hljs-keyword">self</span>.reader.read(&amp;<span class="hljs-keyword">mut</span> utf8_buffer);

        <span class="hljs-keyword">match</span> from_utf8(&amp;utf8_buffer) {
            <span class="hljs-literal">Ok</span>(string) =&gt; {
                <span class="hljs-keyword">self</span>.character_buffer = string.chars().collect();
                <span class="hljs-keyword">self</span>.character_buffer.pop_front()
            }
            <span class="hljs-literal">Err</span>(error) =&gt; {
                <span class="hljs-comment">// Read valid bytes, and rewind the buffered reader for</span>
                <span class="hljs-comment">// the remaining bytes so that they can be read again in the</span>
                <span class="hljs-comment">// next iteration.</span>

                <span class="hljs-keyword">let</span> valid_bytes = error.valid_up_to();
                <span class="hljs-keyword">let</span> string = from_utf8(&amp;utf8_buffer[..valid_bytes]).unwrap();

                <span class="hljs-keyword">let</span> remaining_bytes = <span class="hljs-number">4</span> - valid_bytes;

                <span class="hljs-keyword">let</span> _ = <span class="hljs-keyword">self</span>.reader.seek_relative(-(remaining_bytes <span class="hljs-keyword">as</span> <span class="hljs-built_in">i64</span>));

                <span class="hljs-comment">// Collect the valid characters into character_buffer</span>
                <span class="hljs-keyword">self</span>.character_buffer = string.chars().collect();

                <span class="hljs-comment">// Return the first character from character_buffer</span>
                <span class="hljs-keyword">self</span>.character_buffer.pop_front()
            }
        }
    }
}
</code></pre>
<p>And that's all you need to do for reading the input data for parsing. It's time to move on to the next stage in the process.</p>
<h2 id="heading-how-to-build-a-json-parser-stage-2-prepare-intermediate-data-types">How to Build a JSON Parser – Stage 2: Prepare Intermediate Data Types</h2>
<p>This isn't really a stage in the parsing pipeline, but it is a prerequisite for the next steps. We need to define Rust types that map to all of the possible types that JSON supports.</p>
<p>JSON supports the following data types:</p>
<ul>
<li>String</li>
<li>Number</li>
<li>Boolean</li>
<li>Array</li>
<li>Object</li>
<li>Null</li>
</ul>
<p>A number can further be either an integer, or a floating-point number. While you can use <code>f64</code> as the Rust type for all JSON numbers, practically it's not feasible without littering your code with type casts everywhere when you try to use it. </p>
<p>So in this tutorial, we're going to indeed make that distinction and record that fact.</p>
<h3 id="heading-the-value-type">The value type</h3>
<p>Enums are the ideal way to store state like this, where each variant needs to have some identifier as metadata (in this case the type of JSON value), and optionally some data attached to it. The data you're going to attach to these variants will be the actual value of that type in JSON.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/value.rs</span>

<span class="hljs-keyword">use</span> std::collections::HashMap;

<span class="hljs-meta">#[derive(Debug, Copy, Clone, PartialEq)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">Number</span></span> {
    I64(<span class="hljs-built_in">i64</span>),
    F64(<span class="hljs-built_in">f64</span>),
}

<span class="hljs-meta">#[derive(Debug, PartialEq, Clone)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">Value</span></span> {
    <span class="hljs-built_in">String</span>(<span class="hljs-built_in">String</span>),
    Number(Number),
    Boolean(<span class="hljs-built_in">bool</span>),
    Array(<span class="hljs-built_in">Vec</span>&lt;Value&gt;),
    Object(HashMap&lt;<span class="hljs-built_in">String</span>, Value&gt;),
    Null,
}
</code></pre>
<p>The first few variants are pretty straightforward, you define the variant and the data it holds is a corresponding Rust type. The last variant is even simpler, representing the <code>null</code> value which doesn't need further data to be stored.</p>
<p>The <code>Array</code> and <code>Object</code> variants though are a bit more interesting, since they are recursively storing the Enum itself. This makes sense, as arrays in JSON can have any value type that JSON spec supports. And objects in JSON always have string keys and any JSON supported value, including other objects.</p>
<h3 id="heading-how-to-add-helpful-conversion-methods">How to add helpful conversion methods</h3>
<p>You will also need a way to convert the enum type into the underlying types, and throw an error if the underlying data isn't what you expected. This is mostly boilerplate code, so I'm just going to put it all together without further explanation:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/value.rs</span>

<span class="hljs-keyword">impl</span> TryFrom&lt;&amp;Value&gt; <span class="hljs-keyword">for</span> <span class="hljs-built_in">String</span> {
    <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Error</span></span> = ();

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">try_from</span></span>(value: &amp;Value) -&gt; <span class="hljs-built_in">Result</span>&lt;<span class="hljs-keyword">Self</span>, ()&gt; {
        <span class="hljs-keyword">match</span> value {
            Value::<span class="hljs-built_in">String</span>(value) =&gt; <span class="hljs-literal">Ok</span>(value.clone()),
            _ =&gt; <span class="hljs-literal">Err</span>(()),
        }
    }
}

<span class="hljs-keyword">impl</span> TryFrom&lt;&amp;Value&gt; <span class="hljs-keyword">for</span> <span class="hljs-built_in">i64</span> {
    <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Error</span></span> = ();

    <span class="hljs-meta">#[allow(clippy::cast_possible_truncation)]</span>
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">try_from</span></span>(value: &amp;Value) -&gt; <span class="hljs-built_in">Result</span>&lt;<span class="hljs-keyword">Self</span>, ()&gt; {
        <span class="hljs-keyword">match</span> value {
            Value::Number(value) =&gt; <span class="hljs-keyword">match</span> value {
                Number::I64(value) =&gt; <span class="hljs-literal">Ok</span>(*value),
                Number::F64(value) =&gt; <span class="hljs-literal">Ok</span>(*value <span class="hljs-keyword">as</span> <span class="hljs-built_in">i64</span>),
            },
            _ =&gt; <span class="hljs-literal">Err</span>(()),
        }
    }
}

<span class="hljs-keyword">impl</span> TryFrom&lt;&amp;Value&gt; <span class="hljs-keyword">for</span> <span class="hljs-built_in">f64</span> {
    <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Error</span></span> = ();

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">try_from</span></span>(value: &amp;Value) -&gt; <span class="hljs-built_in">Result</span>&lt;<span class="hljs-keyword">Self</span>, ()&gt; {
        <span class="hljs-keyword">match</span> value {
            Value::Number(value) =&gt; <span class="hljs-keyword">match</span> value {
                Number::F64(value) =&gt; <span class="hljs-literal">Ok</span>(*value),
                Number::I64(value) =&gt; <span class="hljs-literal">Ok</span>(*value <span class="hljs-keyword">as</span> <span class="hljs-built_in">f64</span>),
            },
            _ =&gt; <span class="hljs-literal">Err</span>(()),
        }
    }
}

<span class="hljs-keyword">impl</span> TryFrom&lt;&amp;Value&gt; <span class="hljs-keyword">for</span> <span class="hljs-built_in">bool</span> {
    <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Error</span></span> = ();

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">try_from</span></span>(value: &amp;Value) -&gt; <span class="hljs-built_in">Result</span>&lt;<span class="hljs-keyword">Self</span>, ()&gt; {
        <span class="hljs-keyword">match</span> value {
            Value::Boolean(value) =&gt; <span class="hljs-literal">Ok</span>(*value),
            _ =&gt; <span class="hljs-literal">Err</span>(()),
        }
    }
}

<span class="hljs-keyword">impl</span>&lt;<span class="hljs-symbol">'a</span>&gt; TryFrom&lt;&amp;<span class="hljs-symbol">'a</span> Value&gt; <span class="hljs-keyword">for</span> &amp;<span class="hljs-symbol">'a</span> <span class="hljs-built_in">Vec</span>&lt;Value&gt; {
    <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Error</span></span> = ();

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">try_from</span></span>(value: &amp;<span class="hljs-symbol">'a</span> Value) -&gt; <span class="hljs-built_in">Result</span>&lt;<span class="hljs-keyword">Self</span>, ()&gt; {
        <span class="hljs-keyword">match</span> value {
            Value::Array(value) =&gt; <span class="hljs-literal">Ok</span>(value),
            _ =&gt; <span class="hljs-literal">Err</span>(()),
        }
    }
}

<span class="hljs-meta">#[allow(clippy::implicit_hasher)]</span>
<span class="hljs-keyword">impl</span>&lt;<span class="hljs-symbol">'a</span>&gt; TryFrom&lt;&amp;<span class="hljs-symbol">'a</span> Value&gt; <span class="hljs-keyword">for</span> &amp;<span class="hljs-symbol">'a</span> HashMap&lt;<span class="hljs-built_in">String</span>, Value&gt; {
    <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Error</span></span> = ();

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">try_from</span></span>(value: &amp;<span class="hljs-symbol">'a</span> Value) -&gt; <span class="hljs-built_in">Result</span>&lt;<span class="hljs-keyword">Self</span>, ()&gt; {
        <span class="hljs-keyword">match</span> value {
            Value::Object(value) =&gt; <span class="hljs-literal">Ok</span>(value),
            _ =&gt; <span class="hljs-literal">Err</span>(()),
        }
    }
}
</code></pre>
<h2 id="heading-how-to-build-a-json-parser-stage-3-tokenization">How to Build a JSON Parser – Stage 3: Tokenization</h2>
<p>The next step is to take the input data and tokenize it. </p>
<p>Tokenization is the process of splitting a large chunk of input into smaller, more digestible units that can then be analysed independently. This also allows you to work with them much more easily than just byte streams and they help represent the incoming data as a standard form, and allow for mapping tokens to output value types.</p>
<p>The parser can then recursively process all tokens until there's nothing to process, giving us the parsed data once it finishes.</p>
<h3 id="heading-how-to-define-expected-valid-tokens">How to define expected valid tokens</h3>
<p>There is going to be some duplication here compared to the value type you looked at previously, but that's to be expected, since the token representation of any literal value will be that value itself. There's no way to break it down to smaller units in that case.</p>
<p>Once again, Enum is the right data type for this since we need both metadata (as the token type), and optionally data associated with it.</p>
<p>The tokens representing literal values can be defined in this way:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/token.rs</span>

<span class="hljs-keyword">use</span> std::io::{Read, Seek};
<span class="hljs-keyword">use</span> std::iter::Peekable;
<span class="hljs-keyword">use</span> crate::reader::JsonReader;

<span class="hljs-meta">#[derive(Debug, Copy, Clone, PartialEq)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">Number</span></span> {
    I64(<span class="hljs-built_in">i64</span>),
    F64(<span class="hljs-built_in">f64</span>),
}

<span class="hljs-meta">#[derive(Debug, Clone, PartialEq)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">Token</span></span> {
    <span class="hljs-built_in">String</span>(<span class="hljs-built_in">String</span>),
    Number(Number),
    Boolean(<span class="hljs-built_in">bool</span>),
    Null,
}
</code></pre>
<p>Apart from these, we also have a lot of other tokens in JSON that form the "grammar" of the JSON format. These are:</p>
<ul>
<li>Curly braces (<code>{</code> or <code>}</code> ) that represent opening and closing of an object respectively.</li>
<li>Square brackets (<code>[</code> or <code>]</code>) that represent opening and closing of an array respectively.</li>
<li>Colon (<code>:</code>) for separating key-value pairs within the object.</li>
<li>Comma (<code>,</code>) for separating values.</li>
<li>Quotes (<code>"</code>) that represent opening/closing of the string literal values.</li>
</ul>
<p>All of these do not need to have any data associated with them, so they're going to be unit variants in the enum. Adding these in, the complete enum will be:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/token.rs</span>

<span class="hljs-keyword">use</span> std::io::{Read, Seek};
<span class="hljs-keyword">use</span> std::iter::Peekable;
<span class="hljs-keyword">use</span> crate::reader::JsonReader;
<span class="hljs-keyword">use</span> crate::value::Number;

<span class="hljs-meta">#[derive(Debug, Clone, PartialEq)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-title">Token</span></span> {
    CurlyOpen,
    CurlyClose,
    Quotes,
    Colon,
    <span class="hljs-built_in">String</span>(<span class="hljs-built_in">String</span>),
    Number(Number),
    ArrayOpen,
    ArrayClose,
    Comma,
    Boolean(<span class="hljs-built_in">bool</span>),
    Null,
}
</code></pre>
<h3 id="heading-how-to-implement-the-tokenizer-struct">How to implement the tokenizer struct</h3>
<p>You are going to need a <code>JsonTokenizer</code> struct that can facilitate the process while also responsible for holding the state of the tokenizer process:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/token.rs</span>

<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">JsonTokenizer</span></span>&lt;T&gt;
    <span class="hljs-keyword">where</span>
        T: Read + Seek,
{
    tokens: <span class="hljs-built_in">Vec</span>&lt;Token&gt;,
    iterator: Peekable&lt;JsonReader&lt;T&gt;&gt;,
}

<span class="hljs-keyword">impl</span>&lt;T&gt; JsonTokenizer&lt;T&gt;
<span class="hljs-keyword">where</span>
    T: Read + Seek,
{
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">new</span></span>(reader: File) -&gt; JsonTokenizer&lt;File&gt; {
        <span class="hljs-keyword">let</span> json_reader = JsonReader::&lt;File&gt;::new(BufReader::new(reader));

        JsonTokenizer {
            iterator: json_reader.peekable(),
            tokens: <span class="hljs-built_in">vec!</span>[],
        }
    }

    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">from_bytes</span></span>&lt;<span class="hljs-symbol">'a</span>&gt;(input: &amp;<span class="hljs-symbol">'a</span> [<span class="hljs-built_in">u8</span>]) -&gt; JsonTokenizer&lt;Cursor&lt;&amp;<span class="hljs-symbol">'a</span> [<span class="hljs-built_in">u8</span>]&gt;&gt; {
        <span class="hljs-keyword">let</span> json_reader = JsonReader::&lt;Cursor&lt;&amp;<span class="hljs-symbol">'a</span> [<span class="hljs-built_in">u8</span>]&gt;&gt;::from_bytes(input);

        JsonTokenizer {
            iterator: json_reader.peekable(),
            tokens: <span class="hljs-built_in">Vec</span>::with_capacity(input.len()),
        }
    }
}
</code></pre>
<p>In this case, we've made it generic over where the input comes from. The type T needs to implement <code>Read</code> &amp; <code>Seek</code> traits, the reason for which is explained shortly.</p>
<p>The iterator also needs to be <code>Peekable</code>, which basically means we should be able to read the next item in the iterator without advancing the iterator itself.</p>
<h3 id="heading-how-to-tokenize-an-iterator-of-characters">How to tokenize an iterator of characters</h3>
<p>Once you've defined all of the expected tokens, you need to take your character iterator and convert it into a list of tokens, where each entry is a variant of the <code>Token</code> enum defined in the last section.</p>
<p>We'll start by writing a skeleton function that matches on the incoming character and panics if it encounters an invalid token:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/token.rs</span>

<span class="hljs-keyword">impl</span>&lt;T&gt; JsonTokenizer&lt;T&gt; <span class="hljs-keyword">where</span>
    T: Read + Seek, {
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">tokenize_json</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">Result</span>&lt;&amp;[Token], ()&gt; {
        <span class="hljs-keyword">while</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(character) = <span class="hljs-keyword">self</span>.iterator.peek() {
            <span class="hljs-keyword">match</span> *character {
                <span class="hljs-comment">// Parse all other tokens here</span>
                <span class="hljs-comment">// ...</span>
                character =&gt; {
                    <span class="hljs-keyword">if</span> character.is_ascii_whitespace() {
                        <span class="hljs-keyword">continue</span>;
                    }

                    <span class="hljs-built_in">panic!</span>(<span class="hljs-string">"Unexpected character: ;{character};"</span>)
                }
            }
        }

        <span class="hljs-literal">Ok</span>(&amp;<span class="hljs-keyword">self</span>.tokens)
    }
}
</code></pre>
<p>There are two noteworthy things here, let's start with the easy one. If your match block doesn't encounter any known characters (you will implement this shortly), you need to have a "catch-all" condition that matches any character. </p>
<p>Here, we are going to ignore any whitespace characters and continue to the next iteration if it encounters one. If the character isn't a whitespace, then you need to panic (or return error) here.</p>
<p>The next noteworthy thing here is <code>self.iterator.peek()</code>. To facilitate parsing of different kinds of tokens from delimiters to literal values, it is important that the iterator is not advanced when reading out the next character. This needs to happen so that you can conditionally advance it based on what character is next. </p>
<p>You also need to delegate parsing of certain sets of tokens to different functions, which will have their own logic of advancing the iterator. </p>
<p>A good example is parsing the <code>null</code> literal value. If the match encounters a <code>n</code> character and is not within a string, object, number, and so on, then you need to ensure that the next three characters are <code>u</code>, <code>l</code>, <code>l</code> respectively to form the literal value <code>null</code> and then advance the iterator by four so that the next loop starts parsing after the <code>null</code> character and not in the middle of it.</p>
<h3 id="heading-how-to-parse-string-tokens">How to parse string tokens</h3>
<p>We're going to start by parsing strings. Let's stop for a second and think what needs to happen step-by-step:</p>
<ul>
<li>Check if match encounters a <code>"</code> character. If it does, push <code>Token::Quote</code> to your list of output tokens.</li>
<li>Advance the iterator by one so the next steps start from after the <code>"</code> character.</li>
<li>Parse all characters as part of the string until you encounter another <code>"</code> character which indicates closing of the string value.</li>
<li>Advance the iterator by however many characters are parsed as part of the string, and one addition to also jump over the closing <code>"</code> character.</li>
<li>Push <code>Token::String</code> with the parsed value to your list of output tokens.</li>
<li>Push <code>Token::Quote</code> to your list of output tokens.</li>
</ul>
<p>Hopefully, that isn't too confusing. But the code should help you understand it better:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/token.rs</span>

<span class="hljs-keyword">impl</span>&lt;T&gt; JsonTokenizer&lt;T&gt;
    <span class="hljs-keyword">where</span>
        T: Read + Seek,
{
    <span class="hljs-comment">// ...</span>

    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">tokenize_json</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">Result</span>&lt;&amp;[Token], ()&gt; {
        <span class="hljs-keyword">while</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(character) = <span class="hljs-keyword">self</span>.iterator.peek() {
            <span class="hljs-keyword">match</span> *character {
                <span class="hljs-string">'"'</span> =&gt; {
                    <span class="hljs-comment">// Pushed opening quote to output tokens list.</span>
                    <span class="hljs-keyword">self</span>.tokens.push(Token::Quotes);

                    <span class="hljs-comment">// Skip quote token since we already added it to the tokens list.</span>
                    <span class="hljs-keyword">let</span> _ = <span class="hljs-keyword">self</span>.iterator.next();

                    <span class="hljs-comment">// Delegate parsing string value to a separate function.</span>
                    <span class="hljs-comment">// The function should also take care of advancing the iterator properly.</span>
                    <span class="hljs-keyword">let</span> string = <span class="hljs-keyword">self</span>.parse_string();

                    <span class="hljs-comment">// Push parsed string to output tokens list.</span>
                    <span class="hljs-keyword">self</span>.tokens.push(Token::<span class="hljs-built_in">String</span>(string));

                    <span class="hljs-comment">// Pushed closing quote to output tokens list.</span>
                    <span class="hljs-keyword">self</span>.tokens.push(Token::Quotes);
                }
                <span class="hljs-comment">// ...</span>
            }
        }

        <span class="hljs-literal">Ok</span>(&amp;<span class="hljs-keyword">self</span>.tokens)
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">parse_string</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">String</span> {
        <span class="hljs-comment">// Create new vector to hold parsed characters.</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> string_characters = <span class="hljs-built_in">Vec</span>::&lt;<span class="hljs-built_in">char</span>&gt;::new();

        <span class="hljs-comment">// Take each character by reference so that they</span>
        <span class="hljs-comment">// aren't moved out of the iterator, which will</span>
        <span class="hljs-comment">// require you to move the iterator into this</span>
        <span class="hljs-comment">// function.</span>
        <span class="hljs-keyword">for</span> character <span class="hljs-keyword">in</span> <span class="hljs-keyword">self</span>.iterator.by_ref() {
            <span class="hljs-comment">// If it encounters a closing `"`, break</span>
            <span class="hljs-comment">// out of the loop as the string has ended.</span>
            <span class="hljs-keyword">if</span> character == <span class="hljs-string">'"'</span> {
                <span class="hljs-keyword">break</span>;
            }

            <span class="hljs-comment">// Continue pushing to the vector to build</span>
            <span class="hljs-comment">// the string.</span>
            string_characters.push(character);
        }

        <span class="hljs-comment">// Create a string out of character iterator and</span>
        <span class="hljs-comment">// return it.</span>
        <span class="hljs-built_in">String</span>::from_iter(string_characters)
    }
}
</code></pre>
<p>As I've previously mentioned, we're not going to look at handling escape characters in this tutorial, as they do not add much value towards learning the topic at hand, but if you're interested, it will be a good exercise for you to add that in on top of the implementation.</p>
<p>That takes care of parsing the string, we can move on to a more interesting value type next.</p>
<h3 id="heading-how-to-parse-number-tokens">How to parse number tokens</h3>
<p>Numbers in JSON spec have a lot of variation. They can either be positive or negative, and integers or decimals. They can also be defined as scientific notation (for example negative exponential <code>3.7e-5</code> or positive exponential <code>3.7e5</code>). And we need to parse all of these variations.</p>
<p>As always, we'll start with the easy bit. If we encounter any character that can be a valid character in number, you need to delegate parsing to a <code>parse_number</code> function. But also, any valid number can only start with either a digit, or a negative sign. A number cannot begin with a decimal character or an epsilon character, so it makes things easier for us.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/token.rs</span>

<span class="hljs-keyword">impl</span>&lt;T&gt; JsonTokenizer&lt;T&gt;
    <span class="hljs-keyword">where</span>
        T: Read + Seek,
{
    <span class="hljs-comment">// ...</span>

    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">tokenize_json</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">Result</span>&lt;&amp;[Token], ()&gt; {
        <span class="hljs-keyword">while</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(character) = <span class="hljs-keyword">self</span>.iterator.peek() {
            <span class="hljs-keyword">match</span> *character {
                <span class="hljs-comment">// ...</span>

                <span class="hljs-string">'-'</span> | <span class="hljs-string">'0'</span>..=<span class="hljs-string">'9'</span> =&gt; {
                    <span class="hljs-keyword">let</span> number = <span class="hljs-keyword">self</span>.parse_number()?;
                    <span class="hljs-keyword">self</span>.tokens.push(Token::Number(number));
                }

                <span class="hljs-comment">// ...</span>
            }
        }

        <span class="hljs-literal">Ok</span>(&amp;<span class="hljs-keyword">self</span>.tokens)
    }

    <span class="hljs-comment">// ...</span>
}
</code></pre>
<p>Next, we'll implement the <code>parse_number</code> method:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/token.rs</span>

<span class="hljs-keyword">impl</span>&lt;T&gt; JsonTokenizer&lt;T&gt;
    <span class="hljs-keyword">where</span>
        T: Read + Seek,
{
    <span class="hljs-comment">// ...</span>

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">parse_number</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">Result</span>&lt;Number, ()&gt; {
        <span class="hljs-comment">// Store parsed number characters.</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> number_characters = <span class="hljs-built_in">Vec</span>::&lt;<span class="hljs-built_in">char</span>&gt;::new();

        <span class="hljs-comment">// Stores whether the digit being parsed is after a `.` character</span>
        <span class="hljs-comment">// making it a decimal.</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> is_decimal = <span class="hljs-literal">false</span>;

        <span class="hljs-comment">// Stores the characters after an epsilon character `e` or `E`</span>
        <span class="hljs-comment">// to indicate the exponential value.</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> epsilon_characters = <span class="hljs-built_in">Vec</span>::&lt;<span class="hljs-built_in">char</span>&gt;::new();

        <span class="hljs-comment">// Stores whether the digit being parsed is part of the epsilon</span>
        <span class="hljs-comment">// characters.</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> is_epsilon_characters = <span class="hljs-literal">false</span>;

        <span class="hljs-keyword">while</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(character) = <span class="hljs-keyword">self</span>.iterator.peek() {
            <span class="hljs-keyword">match</span> character {
                <span class="hljs-comment">// Match the negative sign character that indicates whether number is negative</span>
                <span class="hljs-string">'-'</span> =&gt; {
                    <span class="hljs-keyword">if</span> is_epsilon_characters {
                        <span class="hljs-comment">// If it's parsing epsilon characters, push it to the epsilon</span>
                        <span class="hljs-comment">// character set.</span>
                        epsilon_characters.push(<span class="hljs-string">'-'</span>);
                    } <span class="hljs-keyword">else</span> {
                        <span class="hljs-comment">// Otherwise, push it to normal character set.</span>
                        number_characters.push(<span class="hljs-string">'-'</span>);
                    }

                    <span class="hljs-comment">// Advance the iterator by 1.</span>
                    <span class="hljs-keyword">let</span> _ = <span class="hljs-keyword">self</span>.iterator.next();
                }
                <span class="hljs-comment">// Match a positive sign, which can be treated as redundant and ignored since</span>
                <span class="hljs-comment">// positive is the default.</span>
                <span class="hljs-string">'+'</span> =&gt; {
                    <span class="hljs-comment">// Advance the iterator by 1.</span>
                    <span class="hljs-keyword">let</span> _ = <span class="hljs-keyword">self</span>.iterator.next();
                }
                <span class="hljs-comment">// Match any digit between 0 and 9, and store it into the `digit`</span>
                <span class="hljs-comment">// variable.</span>
                digit @ <span class="hljs-string">'0'</span>..=<span class="hljs-string">'9'</span> =&gt; {
                    <span class="hljs-keyword">if</span> is_epsilon_characters {
                        <span class="hljs-comment">// If it's parsing epsilon characters, push it to the epsilon</span>
                        <span class="hljs-comment">// character set.</span>
                        epsilon_characters.push(*digit);
                    } <span class="hljs-keyword">else</span> {
                        <span class="hljs-comment">// Otherwise, push it to normal character set.</span>
                        number_characters.push(*digit);
                    }
                    <span class="hljs-comment">// Advance the iterator by 1.</span>
                    <span class="hljs-keyword">let</span> _ = <span class="hljs-keyword">self</span>.iterator.next();
                }
                <span class="hljs-comment">// Match the period character which indicates start of the fractional</span>
                <span class="hljs-comment">// part of a decimal number.</span>
                <span class="hljs-string">'.'</span> =&gt; {
                    <span class="hljs-comment">// Push the decimal character to numbers character set.</span>
                    number_characters.push(<span class="hljs-string">'.'</span>);

                    <span class="hljs-comment">// Set the current state of number being decimal to true.</span>
                    is_decimal = <span class="hljs-literal">true</span>;

                    <span class="hljs-comment">// Advance the iterator by 1.</span>
                    <span class="hljs-keyword">let</span> _ = <span class="hljs-keyword">self</span>.iterator.next();
                }
                <span class="hljs-comment">// Match any of the characters that can signify end of the number</span>
                <span class="hljs-comment">// literal value. This can be a comma which separates key-value pair,</span>
                <span class="hljs-comment">// closing object character, closing array character, or a `:` which</span>
                <span class="hljs-comment">// separates a key from its value.</span>
                <span class="hljs-string">'}'</span> | <span class="hljs-string">','</span> | <span class="hljs-string">']'</span> | <span class="hljs-string">':'</span> =&gt; {
                    <span class="hljs-keyword">break</span>;
                }
                <span class="hljs-comment">// Match the epsilon character which indicates that the number is in</span>
                <span class="hljs-comment">// scientific notation.</span>
                <span class="hljs-string">'e'</span> | <span class="hljs-string">'E'</span> =&gt; {
                    <span class="hljs-comment">// Panic if it's already parsing an exponential number since this would</span>
                    <span class="hljs-comment">// mean there are 2 epsilon characters which is invalid.</span>
                    <span class="hljs-keyword">if</span> is_epsilon_characters {
                        <span class="hljs-built_in">panic!</span>(<span class="hljs-string">"Unexpected character while parsing number: {character}. Double epsilon characters encountered"</span>);
                    }

                    <span class="hljs-comment">// Set the current state of number being in scientific notation to true.</span>
                    is_epsilon_characters = <span class="hljs-literal">true</span>;

                    <span class="hljs-comment">// Advance the iterator by 1.</span>
                    <span class="hljs-keyword">let</span> _ = <span class="hljs-keyword">self</span>.iterator.next();
                }
                <span class="hljs-comment">// Panic if any other character is encountered.</span>
                other =&gt; {
                    <span class="hljs-keyword">if</span> !other.is_ascii_whitespace() {
                        <span class="hljs-built_in">panic!</span>(<span class="hljs-string">"Unexpected character while parsing number: {character}"</span>)
                    } <span class="hljs-keyword">else</span> {
                        <span class="hljs-keyword">self</span>.iterator.next();
                    }
                },
            }
        }

        <span class="hljs-keyword">if</span> is_epsilon_characters {
            <span class="hljs-comment">// if the number is an exponential, perform the calculations to convert it</span>
            <span class="hljs-comment">// to a floating point number in rust.</span>

            <span class="hljs-comment">// Parse base as floating point number.</span>
            <span class="hljs-keyword">let</span> base: <span class="hljs-built_in">f64</span> = <span class="hljs-built_in">String</span>::from_iter(number_characters).parse().unwrap();

            <span class="hljs-comment">// Parse exponential as floating point number.</span>
            <span class="hljs-keyword">let</span> exponential: <span class="hljs-built_in">f64</span> = <span class="hljs-built_in">String</span>::from_iter(epsilon_characters).parse().unwrap();

            <span class="hljs-comment">// Return the final computed decimal number.</span>
            <span class="hljs-literal">Ok</span>(Number::F64(base * <span class="hljs-number">10_f64</span>.powf(exponential)))
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> is_decimal {
            <span class="hljs-comment">// if the number is a decimal, parse it as a floating point number in rust.</span>
            <span class="hljs-literal">Ok</span>(Number::F64(
                <span class="hljs-built_in">String</span>::from_iter(number_characters).parse::&lt;<span class="hljs-built_in">f64</span>&gt;().unwrap(),
            ))
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-comment">// Parse the number as an integer in rust.</span>
            <span class="hljs-literal">Ok</span>(Number::I64(
                <span class="hljs-built_in">String</span>::from_iter(number_characters).parse::&lt;<span class="hljs-built_in">i64</span>&gt;().unwrap(),
            ))
        }
    }
}
</code></pre>
<p>It is advisable for you to go through the code and read the comments to understand this function. You shouldn't encounter any new syntax that is not either covered already or assumed to be known by the reader.</p>
<h3 id="heading-how-to-parse-boolean-tokens">How to parse boolean tokens</h3>
<p>Parsing booleans is going to be the simplest one we look at so far. All we need to do is match <code>t</code> or <code>f</code> as the first character, and then check the next few characters to ensure they form the literal value <code>true</code> or <code>false</code>.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/token.rs</span>

<span class="hljs-keyword">impl</span>&lt;T&gt; JsonTokenizer&lt;T&gt;
    <span class="hljs-keyword">where</span>
        T: Read + Seek,
{
    <span class="hljs-comment">// ...</span>

    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">tokenize_json</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">Result</span>&lt;&amp;[Token], ()&gt; {
        <span class="hljs-keyword">while</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(character) = <span class="hljs-keyword">self</span>.iterator.peek() {
            <span class="hljs-keyword">match</span> *character {
                <span class="hljs-comment">// ...</span>

                <span class="hljs-comment">// Match `t` character which indicates beginning of a boolean literal.</span>
                <span class="hljs-string">'t'</span> =&gt; {
                    <span class="hljs-comment">// Advance iterator by 1.</span>
                    <span class="hljs-keyword">let</span> _ = <span class="hljs-keyword">self</span>.iterator.next();

                    <span class="hljs-comment">// Assert next character is `r` while advancing the iterator by 1.</span>
                    <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-literal">Some</span>(<span class="hljs-string">'r'</span>), <span class="hljs-keyword">self</span>.iterator.next());
                    <span class="hljs-comment">// Assert next character is `u` while advancing the iterator by 1.</span>
                    <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-literal">Some</span>(<span class="hljs-string">'u'</span>), <span class="hljs-keyword">self</span>.iterator.next());
                    <span class="hljs-comment">// Assert next character is `e` while advancing the iterator by 1.</span>
                    <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-literal">Some</span>(<span class="hljs-string">'e'</span>), <span class="hljs-keyword">self</span>.iterator.next());

                    <span class="hljs-comment">// Push the literal value to token list.</span>
                    <span class="hljs-keyword">self</span>.tokens.push(Token::Boolean(<span class="hljs-literal">true</span>));
                }
                <span class="hljs-string">'f'</span> =&gt; {
                    <span class="hljs-comment">// Advance iterator by 1.</span>
                    <span class="hljs-keyword">let</span> _ = <span class="hljs-keyword">self</span>.iterator.next();

                    <span class="hljs-comment">// Assert next character is `a` while advancing the iterator by 1.</span>
                    <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-literal">Some</span>(<span class="hljs-string">'a'</span>), <span class="hljs-keyword">self</span>.iterator.next());
                    <span class="hljs-comment">// Assert next character is `l` while advancing the iterator by 1.</span>
                    <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-literal">Some</span>(<span class="hljs-string">'l'</span>), <span class="hljs-keyword">self</span>.iterator.next());
                    <span class="hljs-comment">// Assert next character is `s` while advancing the iterator by 1.</span>
                    <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-literal">Some</span>(<span class="hljs-string">'s'</span>), <span class="hljs-keyword">self</span>.iterator.next());
                    <span class="hljs-comment">// Assert next character is `e` while advancing the iterator by 1.</span>
                    <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-literal">Some</span>(<span class="hljs-string">'e'</span>), <span class="hljs-keyword">self</span>.iterator.next());

                    <span class="hljs-comment">// Push the literal value to token list.</span>
                    <span class="hljs-keyword">self</span>.tokens.push(Token::Boolean(<span class="hljs-literal">false</span>));
                }

                <span class="hljs-comment">// ...</span>
            }
        }

        <span class="hljs-literal">Ok</span>(&amp;<span class="hljs-keyword">self</span>.tokens)
    }
}
</code></pre>
<h3 id="heading-how-to-parse-null-literal">How to parse Null Literal</h3>
<p>This is very similar to how we parsed booleans in the previous step:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/token.rs</span>

<span class="hljs-keyword">impl</span>&lt;T&gt; JsonTokenizer&lt;T&gt;
    <span class="hljs-keyword">where</span>
        T: Read + Seek,
{
    <span class="hljs-comment">// ...</span>

    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">tokenize_json</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">Result</span>&lt;&amp;[Token], ()&gt; {
        <span class="hljs-keyword">while</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(character) = <span class="hljs-keyword">self</span>.iterator.peek() {
            <span class="hljs-keyword">match</span> *character {
                <span class="hljs-comment">// ...</span>

                <span class="hljs-string">'n'</span> =&gt; {
                    <span class="hljs-comment">// Advance iterator by 1.</span>
                    <span class="hljs-keyword">let</span> _ = <span class="hljs-keyword">self</span>.iterator.next();

                    <span class="hljs-comment">// Assert next character is `u` while advancing the iterator by 1.</span>
                    <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-literal">Some</span>(<span class="hljs-string">'u'</span>), <span class="hljs-keyword">self</span>.iterator.next());
                    <span class="hljs-comment">// Assert next character is `l` while advancing the iterator by 1.</span>
                    <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-literal">Some</span>(<span class="hljs-string">'l'</span>), <span class="hljs-keyword">self</span>.iterator.next());
                    <span class="hljs-comment">// Assert next character is `l` while advancing the iterator by 1.</span>
                    <span class="hljs-built_in">assert_eq!</span>(<span class="hljs-literal">Some</span>(<span class="hljs-string">'l'</span>), <span class="hljs-keyword">self</span>.iterator.next());

                    <span class="hljs-comment">// Push null literal value to output tokens list.</span>
                    <span class="hljs-keyword">self</span>.tokens.push(Token::Null);
                }

                <span class="hljs-comment">// ...</span>
            }
        }

        <span class="hljs-literal">Ok</span>(&amp;<span class="hljs-keyword">self</span>.tokens)
    }
}
</code></pre>
<h3 id="heading-how-to-parse-delimiters">How to parse delimiters</h3>
<p>Parsing delimiters is very simple. All you need to do is to match on them, and push the respective token into the output token list:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/token.rs</span>

<span class="hljs-keyword">impl</span>&lt;T&gt; JsonTokenizer&lt;T&gt;
    <span class="hljs-keyword">where</span>
        T: Read + Seek,
{
    <span class="hljs-comment">// ...</span>

    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">tokenize_json</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">Result</span>&lt;&amp;[Token], ()&gt; {
        <span class="hljs-keyword">while</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(character) = <span class="hljs-keyword">self</span>.iterator.peek() {
            <span class="hljs-keyword">match</span> *character {
                <span class="hljs-comment">// ...</span>

                <span class="hljs-string">'{'</span> =&gt; {
                    <span class="hljs-keyword">self</span>.tokens.push(Token::CurlyOpen);
                    <span class="hljs-keyword">let</span> _ = <span class="hljs-keyword">self</span>.iterator.next();
                }
                <span class="hljs-string">'}'</span> =&gt; {
                    <span class="hljs-keyword">self</span>.tokens.push(Token::CurlyClose);
                    <span class="hljs-keyword">let</span> _ = <span class="hljs-keyword">self</span>.iterator.next();
                }
                <span class="hljs-string">'['</span> =&gt; {
                    <span class="hljs-keyword">self</span>.tokens.push(Token::ArrayOpen);
                    <span class="hljs-keyword">let</span> _ = <span class="hljs-keyword">self</span>.iterator.next();
                }
                <span class="hljs-string">']'</span> =&gt; {
                    <span class="hljs-keyword">self</span>.tokens.push(Token::ArrayClose);
                    <span class="hljs-keyword">let</span> _ = <span class="hljs-keyword">self</span>.iterator.next();
                }
                <span class="hljs-string">','</span> =&gt; {
                    <span class="hljs-keyword">self</span>.tokens.push(Token::Comma);
                    <span class="hljs-keyword">let</span> _ = <span class="hljs-keyword">self</span>.iterator.next();
                }
                <span class="hljs-string">':'</span> =&gt; {
                    <span class="hljs-keyword">self</span>.tokens.push(Token::Colon);
                    <span class="hljs-keyword">let</span> _ = <span class="hljs-keyword">self</span>.iterator.next();
                }

                <span class="hljs-comment">// ...</span>
            }
        }

        <span class="hljs-literal">Ok</span>(&amp;<span class="hljs-keyword">self</span>.tokens)
    }
}
</code></pre>
<h3 id="heading-how-to-parse-a-terminating-character">How to parse a terminating character</h3>
<p>The input can sometimes contain <code>\0</code> as the last character to indicate that the input has ended. This is more commonly known as EOF (End Of File) when dealing with files. It is also referred by other names like "escape sequence" or "null" character.</p>
<p>We need to handle it and break out of our parsing loop if we ever encounter this:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/token.rs</span>

<span class="hljs-keyword">impl</span>&lt;T&gt; JsonTokenizer&lt;T&gt;
    <span class="hljs-keyword">where</span>
        T: Read + Seek,
{
    <span class="hljs-comment">// ...</span>

    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">tokenize_json</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">Result</span>&lt;&amp;[Token], ()&gt; {
        <span class="hljs-keyword">while</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(character) = <span class="hljs-keyword">self</span>.iterator.peek() {
            <span class="hljs-keyword">match</span> *character {
                <span class="hljs-comment">// ...</span>

                <span class="hljs-string">'\0'</span> =&gt; <span class="hljs-keyword">break</span>,
                other =&gt; {
                    <span class="hljs-keyword">if</span> !other.is_ascii_whitespace() {
                        <span class="hljs-built_in">panic!</span>(<span class="hljs-string">"Unexpected token encountered: {other}"</span>)
                    } <span class="hljs-keyword">else</span> {
                        <span class="hljs-keyword">self</span>.iterator.next();
                    }
                },

                <span class="hljs-comment">// ...</span>
            }
        }

        <span class="hljs-literal">Ok</span>(&amp;<span class="hljs-keyword">self</span>.tokens)
    }
}
</code></pre>
<h2 id="heading-how-to-build-a-json-parser-stage-4-from-tokens-to-value">How to Build a JSON Parser  – Stage 4: From Tokens To Value</h2>
<p>Now that you have all the tokens, it's time to move on to the final stage of the process, converting tokens to real values that you can work with in the Rust code.</p>
<p>Start by creating a unit struct, which can be used as the parser. At this stage, we don't need to hold any state for the entirety of the process:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/parser.rs</span>

<span class="hljs-keyword">use</span> std::collections::HashMap;
<span class="hljs-keyword">use</span> std::fs::File;
<span class="hljs-keyword">use</span> std::io::{BufReader, Cursor};
<span class="hljs-keyword">use</span> std::iter::Peekable;
<span class="hljs-keyword">use</span> std::slice::Iter;
<span class="hljs-keyword">use</span> crate::token::{JsonTokenizer, Token};
<span class="hljs-keyword">use</span> crate::value::Value;

<span class="hljs-comment">/// Main parser which is the entrypoint for parsing JSON.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">JsonParser</span></span>;
</code></pre>
<p>We are also going to use this as the public interface for the parser. So let's start by implementing those methods first:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/parser.rs</span>

<span class="hljs-keyword">impl</span> JsonParser {
    <span class="hljs-comment">/// Create a new [`JsonParser`] that parses JSON from bytes.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">parse_from_bytes</span></span>&lt;<span class="hljs-symbol">'a</span>&gt;(input: &amp;<span class="hljs-symbol">'a</span> [<span class="hljs-built_in">u8</span>]) -&gt; <span class="hljs-built_in">Result</span>&lt;Value, ()&gt; {
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> json_tokenizer = JsonTokenizer::&lt;BufReader&lt;Cursor&lt;&amp;[<span class="hljs-built_in">u8</span>]&gt;&gt;&gt;::from_bytes(input);
        <span class="hljs-keyword">let</span> tokens = json_tokenizer.tokenize_json()?;

        <span class="hljs-literal">Ok</span>(Self::tokens_to_value(tokens))
    }

    <span class="hljs-comment">/// Create a new [`JsonParser`] that parses JSON from file.</span>
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">parse</span></span>(reader: File) -&gt; <span class="hljs-built_in">Result</span>&lt;Value, ()&gt; {
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> json_tokenizer = JsonTokenizer::&lt;BufReader&lt;File&gt;&gt;::new(reader);
        <span class="hljs-keyword">let</span> tokens = json_tokenizer.tokenize_json()?;

        <span class="hljs-literal">Ok</span>(Self::tokens_to_value(tokens))
    }
}
</code></pre>
<p>With that out of the way, you first need to implement the <code>tokens_to_value</code> method that these public methods are calling. </p>
<h3 id="heading-how-to-parse-primitives">How to parse primitives</h3>
<p>This method will be responsible for taking an iterator of tokens as input and outputting the <code>Value</code> type you defined previously. This is also pretty straightforward, since the object/array parsing is delegated to separate methods, which we'll look at shortly.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/parser.rs</span>

<span class="hljs-keyword">impl</span> JsonParser {
    <span class="hljs-comment">// ...</span>

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">tokens_to_value</span></span>(tokens: &amp;[Token]) -&gt; Value {
        <span class="hljs-comment">// Create a peekable iterator over tokens</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> iterator = tokens.iter().peekable();

        <span class="hljs-comment">// Initialize final value to null.</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> value = Value::Null;

        <span class="hljs-comment">// Loop while there are tokens in the iterator.</span>
        <span class="hljs-comment">// Note that you do not need to manually handle advancing the</span>
        <span class="hljs-comment">// iterator in this case which is why you can directly call</span>
        <span class="hljs-comment">// `iterator.next()`.</span>
        <span class="hljs-keyword">while</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(token) = iterator.next() {
            <span class="hljs-keyword">match</span> token {
                Token::CurlyOpen =&gt; {
                    value = Value::Object(Self::process_object(&amp;<span class="hljs-keyword">mut</span> iterator));
                }
                Token::<span class="hljs-built_in">String</span>(string) =&gt; {
                    value = Value::<span class="hljs-built_in">String</span>(string.clone());
                }
                Token::Number(number) =&gt; {
                    value = Value::Number(*number);
                }
                Token::ArrayOpen =&gt; {
                    value = Value::Array(Self::process_array(&amp;<span class="hljs-keyword">mut</span> iterator));
                }
                Token::Boolean(boolean) =&gt; value = Value::Boolean(*boolean),
                Token::Null =&gt; value = Value::Null,
                <span class="hljs-comment">// Ignore all delimiters as you don't need to explicitly do anything</span>
                <span class="hljs-comment">// when you encounter them.</span>
                Token::Comma
                | Token::CurlyClose
                | Token::Quotes
                | Token::Colon
                | Token::ArrayClose =&gt; {}
            }
        }

        value
    }
}
</code></pre>
<h3 id="heading-how-to-parse-arrays">How to parse arrays</h3>
<p>Parsing arrays is almost as straightforward as the parsing logic we looked at above. Since arrays are just collection of other JSON values, there's not much logic involved into parsing them, unlike objects.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/parser.rs</span>

<span class="hljs-keyword">impl</span> JsonParser {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">process_array</span></span>(iterator: &amp;<span class="hljs-keyword">mut</span> Peekable&lt;Iter&lt;Token&gt;&gt;) -&gt; <span class="hljs-built_in">Vec</span>&lt;Value&gt; {
        <span class="hljs-comment">// Initialise a vector of JSON Value type to hold the value of</span>
        <span class="hljs-comment">// array that's currently being parsed.</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> internal_value = <span class="hljs-built_in">Vec</span>::&lt;Value&gt;::new();

        <span class="hljs-comment">// Iterate over all tokens provided.</span>
        <span class="hljs-keyword">while</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(token) = iterator.next() {
            <span class="hljs-keyword">match</span> token {
                Token::CurlyOpen =&gt; {
                    internal_value.push(Value::Object(Self::process_object(iterator)));
                }
                Token::<span class="hljs-built_in">String</span>(string) =&gt; internal_value.push(Value::<span class="hljs-built_in">String</span>(string.clone())),
                Token::Number(number) =&gt; internal_value.push(Value::Number(*number)),
                Token::ArrayOpen =&gt; {
                    internal_value.push(Value::Array(Self::process_array(iterator)));
                }
                <span class="hljs-comment">// Break loop if array is closed. Due to recursive nature of process_array,</span>
                <span class="hljs-comment">// we don't need to explicitly check if the closing token matches the opening</span>
                <span class="hljs-comment">// one.</span>
                Token::ArrayClose =&gt; {
                    <span class="hljs-keyword">break</span>;
                }
                Token::Boolean(boolean) =&gt; internal_value.push(Value::Boolean(*boolean)),
                Token::Null =&gt; internal_value.push(Value::Null),
                <span class="hljs-comment">// Ignore delimiters</span>
                Token::Comma | Token::CurlyClose | Token::Quotes | Token::Colon =&gt; {}
            }
        }

        internal_value
    }
}
</code></pre>
<h3 id="heading-how-to-parse-objects">How to parse objects</h3>
<p>Parsing objects is a bit more tricky than the previous value types, since objects come with their own syntax. But there should be no surprises for you, which is why I encourage you to read through the code and the comments below to understand how it works.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/parser.rs</span>

<span class="hljs-keyword">impl</span> JsonParser {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">process_object</span></span>(iterator: &amp;<span class="hljs-keyword">mut</span> Peekable&lt;Iter&lt;Token&gt;&gt;) -&gt; HashMap&lt;<span class="hljs-built_in">String</span>, Value&gt; {
        <span class="hljs-comment">// Whether the item being parsed is a key or a value. The first element</span>
        <span class="hljs-comment">// should always be a key so this is initialised to true.</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> is_key = <span class="hljs-literal">true</span>;

        <span class="hljs-comment">// The current key for which the value is being parsed.</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> current_key: <span class="hljs-built_in">Option</span>&lt;&amp;<span class="hljs-built_in">str</span>&gt; = <span class="hljs-literal">None</span>;

        <span class="hljs-comment">// The current state of parsed object.</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> value = HashMap::&lt;<span class="hljs-built_in">String</span>, Value&gt;::new();

        <span class="hljs-keyword">while</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(token) = iterator.next() {
            <span class="hljs-keyword">match</span> token {
                <span class="hljs-comment">// If it is a nested object, recursively parse it and store</span>
                <span class="hljs-comment">// in the hashmap with current key.</span>
                Token::CurlyOpen =&gt; {
                    <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(current_key) = current_key {
                        value.insert(
                            current_key.to_string(),
                            Value::Object(Self::process_object(iterator)),
                        );
                    }
                }
                <span class="hljs-comment">// If this token is encountered, break the loop since it</span>
                <span class="hljs-comment">// indicates end of an object being parsed.</span>
                Token::CurlyClose =&gt; {
                    <span class="hljs-keyword">break</span>;
                }
                Token::Quotes | Token::ArrayClose =&gt; {}
                <span class="hljs-comment">// If the token is a colon, it is the separator between key</span>
                <span class="hljs-comment">// and value pair. So the item being parsed from this point</span>
                <span class="hljs-comment">// ahead will not be a key.</span>
                Token::Colon =&gt; {
                    is_key = <span class="hljs-literal">false</span>;
                }
                Token::<span class="hljs-built_in">String</span>(string) =&gt; {
                    <span class="hljs-keyword">if</span> is_key {
                        <span class="hljs-comment">// If the process is presently parsing key, set the value</span>
                        <span class="hljs-comment">// as current key.</span>
                        current_key = <span class="hljs-literal">Some</span>(string);
                    } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(key) = current_key {
                        <span class="hljs-comment">// If the process already has a key set for present item,</span>
                        <span class="hljs-comment">// parse string as value instead, and set the current_key to none</span>
                        <span class="hljs-comment">// once done to prepare for the next key-value pair.</span>
                        value.insert(key.to_string(), Value::<span class="hljs-built_in">String</span>(string.clone()));
                        <span class="hljs-comment">// Set current_key to None to prepare for the next key-value pair.</span>
                        current_key = <span class="hljs-literal">None</span>;
                    }
                }
                Token::Number(number) =&gt; {
                    <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(key) = current_key {
                        value.insert(key.to_string(), Value::Number(*number));
                        <span class="hljs-comment">// Set current_key to None to prepare for the next key-value pair.</span>
                        current_key = <span class="hljs-literal">None</span>;
                    }
                }
                Token::ArrayOpen =&gt; {
                    <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(key) = current_key {
                        value.insert(key.to_string(), Value::Array(Self::process_array(iterator)));
                        <span class="hljs-comment">// Set current_key to None to prepare for the next key-value pair.</span>
                        current_key = <span class="hljs-literal">None</span>;
                    }
                }
                <span class="hljs-comment">// If the token is a comma, it is the separator between multiple key-value pairs</span>
                <span class="hljs-comment">// in JSON. So the item being parsed from this point ahead will be a key.</span>
                Token::Comma =&gt; is_key = <span class="hljs-literal">true</span>,
                Token::Boolean(boolean) =&gt; {
                    <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(key) = current_key {
                        value.insert(key.to_string(), Value::Boolean(*boolean));
                        <span class="hljs-comment">// Set current_key to None to prepare for the next key-value pair.</span>
                        current_key = <span class="hljs-literal">None</span>;
                    }
                }
                Token::Null =&gt; {
                    <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(key) = current_key {
                        value.insert(key.to_string(), Value::Null);
                        <span class="hljs-comment">// Set current_key to None to prepare for the next key-value pair.</span>
                        current_key = <span class="hljs-literal">None</span>;
                    }
                }
            }
        }

        value
    }
}
</code></pre>
<p>And that's it. You should now have everything to start using this to parse a valid JSON file into Rust.</p>
<h2 id="heading-how-to-use-the-json-parser">How to Use the JSON parser</h2>
<p>Let's create a new example in the project to run our JSON parser:</p>
<pre><code class="lang-shell">mkdir examples; touch examples/json.rs
</code></pre>
<p>You also need to register it as an example in the <code>Cargo.toml</code> file:</p>
<pre><code class="lang-toml"><span class="hljs-section">[package]</span>
<span class="hljs-attr">name</span> = <span class="hljs-string">"json-parser"</span>
<span class="hljs-attr">version</span> = <span class="hljs-string">"0.1.0"</span>
<span class="hljs-attr">edition</span> = <span class="hljs-string">"2021"</span>

<span class="hljs-section">[dependencies]</span>

<span class="hljs-section">[[example]]</span>
<span class="hljs-attr">path</span> = <span class="hljs-string">"examples/json.rs"</span>
<span class="hljs-attr">name</span> = <span class="hljs-string">"json"</span>
</code></pre>
<p>Now let's write the code to run for this example. We start by copying over a sample JSON file to the root of the project, which you can find <a target="_blank" href="https://raw.githubusercontent.com/anshulsanghi-blog/json-parser/master/test.json">here</a>.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// examples/json.rs</span>

<span class="hljs-keyword">use</span> std::fs::File;
<span class="hljs-keyword">use</span> json_parser::parser::JsonParser;

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> file = File::open(<span class="hljs-string">"test.json"</span>).unwrap();
    <span class="hljs-keyword">let</span> parser = JsonParser::parse(file).unwrap();

    dbg!(parser);
}
</code></pre>
<p>Running this code using the following command, you should see the same output as below:</p>
<pre><code class="lang-shell">cargo run --example json --release
</code></pre>
<pre><code class="lang-shell">[examples/json.rs:8:5] parser = Object(
    {
        "pairs": Array(
            [
                Object(
                    {
                        "x1": Number(
                            F64(
                                41.844453001935875,
                            ),
                        ),
                        "y0": Number(
                            F64(
                                -33.78221816487377,
                            ),
                        ),
                        "y1": Number(
                            F64(
                                -78.10213222087448,
                            ),
                        ),
                        "x0": Number(
                            F64(
                                95.26235434764715,
                            ),
                        ),
                    },
                ),
                Object(
                    {
                        "x0": Number(
                            F64(
                                115.42029308864215,
                            ),
                        ),
                        "y0": Number(
                            F64(
                                1.2002187300000001e-5,
                            ),
                        ),
                        "x1": Number(
                            F64(
                                83.39640643072113,
                            ),
                        ),
                        "y1": Number(
                            F64(
                                28.643090267505812,
                            ),
                        ),
                    },
                ),
                Object(
                    {
                        "isWorking": Boolean(
                            true,
                        ),
                        "sample": String(
                            "string sample",
                        ),
                        "nullable": Null,
                        "isNotWorking": Boolean(
                            false,
                        ),
                    },
                ),
            ],
        ),
        "utf8": Object(
            {
                "key2": String(
                    "value2",
                ),
                "key1": String(
                    "ࠄࠀࠆࠄࠀࠁࠃ",
                ),
            },
        ),
    },
)
</code></pre>
<p>Congratulations! You've now written your very own JSON parser, while learning some of the advanced use cases of match and iterators in Rust.</p>
<h2 id="heading-wrapping-up"><strong>Wrapping Up</strong></h2>
<p>I hope you can already see interesting ways you can make use of what you've learned today to optimize existing Rust code in your projects, and any future code you write that involves these.</p>
<p>You can find the complete code for everything we looked at in this article in <a target="_blank" href="https://github.com/anshulsanghi-blog/json-parser">this repository</a>.</p>
<p>Also, feel free to <strong><a target="_blank" href="mailto:contact@anshulsanghi.tech">contact me</a></strong> if you have any questions or opinions on this topic.</p>
<h3 id="heading-enjoying-my-work">Enjoying my work?</h3>
<p>Consider buying me a coffee to support my work!</p>
<p><a target="_blank" href="https://buymeacoffee.com/anshulsanghi">☕Buy me a coffee</a>.</p>
<p>'Till next time, happy coding and wishing you clear skies!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Procedural Macros in Rust – A Handbook for Beginners ]]>
                </title>
                <description>
                    <![CDATA[ In this handbook, you'll learn about procedural macros in Rust, and what purposes they serve. You'll also learn how to write your own procedural macros with both hypothetical and real-world examples. This guide assumes that you're familiar with Rust ... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/procedural-macros-in-rust/</link>
                <guid isPermaLink="false">66bb579a5a83db22bea98433</guid>
                
                    <category>
                        <![CDATA[ handbook ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Rust ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Anshul Sanghi ]]>
                </dc:creator>
                <pubDate>Wed, 24 Apr 2024 17:49:17 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/04/Procedural-Macros-in-Rust-Cover--1-.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>In this handbook, you'll learn about procedural macros in Rust, and what purposes they serve. You'll also learn how to write your own procedural macros with both hypothetical and real-world examples.</p>
<p>This guide assumes that you're familiar with Rust and its basic concepts, such as data-types, iterators, and traits. If you need to establish or review your Rust basics, <a target="_blank" href="https://www.freecodecamp.org/news/rust-in-replit/">check out this interactive course</a>.</p>
<p>You don't need any prior knowledge of macros, as this article covers everything from the ground up.</p>
<h2 id="heading-table-of-contents">Table of Contents</h2>
<ol>
<li><a class="post-section-overview" href="#heading-what-are-macros-in-rust">What are Macros in Rust?</a><ol>
<li><a class="post-section-overview" href="#heading-types-of-macros-in-rust">Types of Macros in Rust</a></li>
<li><a class="post-section-overview" href="#heading-types-of-procedural-macros">Types of Procedural Macros</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a><ol>
<li><a class="post-section-overview" href="#heading-helpful-dependencies">Helpful Dependencies</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#heading-how-to-write-a-simple-derive-macro">How to Write a Simple Derive Macro</a><ol>
<li><a class="post-section-overview" href="#heading-the-intostringhashmap-derive-macro">The <code>IntoStringHashMap</code> Derive Macro</a></li>
<li><a class="post-section-overview" href="#heading-how-to-declare-a-derive-macro">How to Declare a Derive Macro</a></li>
<li><a class="post-section-overview" href="#how-to-parse-macro-input">How to Parse Macro Input</a></li>
<li><a class="post-section-overview" href="#how-to-ensure-a-struct-target-for-macro">How to Ensure a Struct Target for the Macro</a></li>
<li><a class="post-section-overview" href="#heading-how-to-build-the-output-code">How to Build the Output Code</a></li>
<li><a class="post-section-overview" href="#heading-how-to-use-your-derive-macro">How to Use Your Derive Macro</a></li>
<li><a class="post-section-overview" href="#heading-how-to-improve-our-implementation">How to Improve Our Implementation</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#heading-a-more-elaborate-derive-macro">A More Elaborate Derive macro</a><ol>
<li><a class="post-section-overview" href="#heading-the-derivecustommodel-macro">The <code>DeriveCustomModel</code> Macro</a></li>
<li><a class="post-section-overview" href="#how-to-separate-implementation-from-declaration">How to Separate Implementation From Declaration</a></li>
<li><a class="post-section-overview" href="#heading-how-to-parse-derive-macro-arguments">How to Parse Derive Macro Arguments</a></li>
<li><a class="post-section-overview" href="#heading-how-to-implement-derivecustommodel">How to Implement <code>DeriveCustomModel</code></a></li>
<li><a class="post-section-overview" href="#heading-how-to-generate-each-custom-model">How to Generate Each Custom Model</a></li>
<li><a class="post-section-overview" href="#how-to-use-your-derivecustommodal-macro">How to Use Your <code>DeriveCustomModal</code> Macro</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#heading-a-simple-attribute-macro">A Simple Attribute Macro</a><ol>
<li><a class="post-section-overview" href="#heading-the-logduration-attribute">The <code>log_duration</code> Attribute</a></li>
<li><a class="post-section-overview" href="#heading-how-to-declare-an-attribute-macro">How to Declare an Attribute Macro</a></li>
<li><a class="post-section-overview" href="#heading-how-to-implement-the-logduration-attribute-macro">How to Implement the <code>log_duration</code> Attribute Macro</a></li>
<li><a class="post-section-overview" href="#how-to-use-your-log-duration-macro">How to Use Your <code>log_duration</code> Macro</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#heading-a-more-elaborate-attribute-macro">A More Elaborate Attribute macro</a><ol>
<li><a class="post-section-overview" href="#heading-the-cachedfn-attribute">The <code>cached_fn</code> Attribute</a></li>
<li><a class="post-section-overview" href="#heading-how-to-implement-the-cachedfn-attribute-macro">How to Implement the <code>cached_fn</code> Attribute Macro</a></li>
<li><a class="post-section-overview" href="#heading-cachedfn-attribute-arguments"><code>cached_fn</code> Attribute Arguments</a></li>
<li><a class="post-section-overview" href="#heading-how-to-use-the-cachedfn-macro">How to Use the <code>cached_fn</code> Macro</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#heading-a-simple-function-like-macro">A Simple Function-like Macro</a><ol>
<li><a class="post-section-overview" href="#heading-the-constantstring-macro">The <code>constant_string</code> Macro</a></li>
<li><a class="post-section-overview" href="#heading-how-to-declare-a-function-like-macro">How to Declare a Function-like Macro</a></li>
<li><a class="post-section-overview" href="#heading-how-to-implement-the-constantstring-macro">How to Implement the <code>constant_string</code> Macro</a></li>
<li><a class="post-section-overview" href="#heading-how-to-use-the-constantstring-macro">How to Use the <code>constant_string</code> Macro</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#heading-a-more-elaborate-function-like-macro">A More Elaborate Function-like Macro</a><ol>
<li><a class="post-section-overview" href="#heading-the-hashmapify-macro">The <code>hash_mapify</code> Macro</a></li>
<li><a class="post-section-overview" href="#heading-how-to-implement-the-hashmapify-macro">How to Implement the <code>hash_mapify</code> Macro</a></li>
<li><a class="post-section-overview" href="#how-to-parse-hash-mapifys-input">How to Parse <code>hash_mapify</code>'s Input</a></li>
<li><a class="post-section-overview" href="#how-to-generate-output-code">How to Generate Output Code</a></li>
<li><a class="post-section-overview" href="#heading-how-to-convert-custom-data-types-to-output-tokens">How to Convert Custom Data Types To Output Tokens</a></li>
<li><a class="post-section-overview" href="#heading-how-to-use-the-hashmapify-macro">How to Use the <code>hash_mapify</code> Macro</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#heading-beyond-writing-macros">Beyond Writing Macros</a><ol>
<li><a class="post-section-overview" href="#heading-helpful-cratestools">Helpful Crates/Tools</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#heading-downsides-of-macros">Downsides of Macros</a><ol>
<li><a class="post-section-overview" href="#heading-debugging-or-lack-thereof">Debugging (or lack thereof)</a></li>
<li><a class="post-section-overview" href="#heading-compile-time-costs">Compile Time Costs</a></li>
<li><a class="post-section-overview" href="#heading-lack-of-auto-complete-and-code-checks">Lack of auto-complete and code checks</a></li>
<li><a class="post-section-overview" href="#heading-where-do-we-draw-the-line">Where do we draw the line?</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a><ol>
<li><a class="post-section-overview" href="#heading-enjoying-my-work">Enjoying my work?</a></li>
</ol>
</li>
</ol>
<h2 id="heading-what-are-macros-in-rust"><strong>What are Macros in Rust?</strong></h2>
<p>Macros are an integral part of the Rust programming language. It doesn’t take long before you start encountering them when first learning the language.</p>
<p>In their simplest form, macros in Rust allow you to execute some code at compile-time. Rust pretty much allows you to do whatever you want when it comes to macros and what you can do with them. The most common use-case of this feature is writing code that generates other code. </p>
<p>Macros are a way to extend functionality of the compiler beyond what's supported as standard. Whether you want to generate code based on existing code, or you want to transform existing code in some form, macros are your go-to tool.</p>
<p>Here's how the official Rust book describes it:</p>
<blockquote>
<p>The term <em>macro</em> refers to a family of features in Rust.  </p>
<p>Fundamentally, macros are a way of writing code that writes other code, which is known as <em>metaprogramming</em>.  </p>
<p>Metaprogramming is useful for reducing the amount of code you have to write and maintain, which is also one of the roles of functions. However, macros have some additional powers that functions don’t.</p>
</blockquote>
<p>Using macros, you can also dynamically add things that are required to be added at compilation time, which is not possible using functions since they get called at runtime. One such feature, for example, is implementing <em>Traits</em> on types, which is required to be implemented at compilation time.</p>
<p>Another advantage of macros is that they can be very flexible, since they can take a dynamic amount of parameters or inputs unlike a function.</p>
<p>Macros do have their own syntax for both writing and using them, which we'll explore in detail in the coming sections.</p>
<p>Some examples of how macros are being used really helps convey just how powerful they are:</p>
<ul>
<li>The <strong>SQLx</strong> project uses macros to verify all your SQL queries and statements (as long as you created them using the provided macro) at compile-time by actually executing them against a running instance of DB (yes, at compile time).</li>
<li><strong>typed_html</strong> implements a complete HTML parser with compile-time validation, all while using the familiar JSX syntax.</li>
</ul>
<h2 id="heading-types-of-macros-in-rust">Types of Macros in Rust</h2>
<p>In Rust, there are 2 different types of macros: declarative and procedural.</p>
<h3 id="heading-declarative-macros">Declarative macros</h3>
<p>Declarative macros work based on syntax parsing. While the official docs define them as allowing you to write syntax extensions, I believe it's more intuitive to consider them as an advanced version of the <code>match</code> keyword for the compiler. </p>
<p>You can define one or more patterns to match, and their body should return the output Rust code you'd like the macro to produce. </p>
<p>We're not going to be talking about them in this article, but if you'd like to learn more, <a target="_blank" href="https://doc.rust-lang.org/reference/macros-by-example.html">this</a> is a good place to start.</p>
<h3 id="heading-procedural-macros">Procedural macros</h3>
<p>These macros, in their most basic use cases, execute any Rust code you want at compile time. The only requirement is that they should take Rust code as input, and return Rust code as output. </p>
<p>There's no special syntax parsing involved for writing these macros (unless you want to do so), which is why they're personally easier for me to understand and write. </p>
<p>Procedural macros are further divided into 3 categories: derive macros, attribute macros, and functional macros.</p>
<h3 id="heading-types-of-procedural-macros">Types of Procedural Macros</h3>
<h4 id="heading-derive-macros">Derive macros</h4>
<p>Derive macros are, generally speaking, applied to data types in Rust. They are a way to extend the type declaration to also automatically "derive" functionality for it. </p>
<p>You can use them to generate "derived" types from a type, or as a way to implement methods on the target data type automatically. This should make sense once you look at the following example below.</p>
<p>Printing non-primitive data types, such as structs, enums or even errors (which are just structs, but let's assume they're not), for debugging purposes is a very common feature for any language, not just Rust. In Rust, only primitives implicitly have the ability to be printed in "debug" contexts. </p>
<p>If you think about how everything in Rust is just traits (even basic operations like add and equals), this makes sense. You want to be able to debug print your custom data types, but Rust has no way of saying "please apply this trait to every single data type in the code out there, ever".</p>
<p>This is where the <code>Debug</code> derive macro comes in. There's a standard way of debug-printing each type of data structure in Rust that it uses for its internal types. The <code>Debug</code> macro allows you to automatically implement the <code>Debug</code> trait for your custom types, while following the same rules and style guide as the implementation for internal data types.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Derive macro examples</span>

<span class="hljs-comment">/// Example for deriving methods on data types</span>
<span class="hljs-meta">#[derive(Debug)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">User</span></span> {
    username: <span class="hljs-built_in">String</span>,
    first_name: <span class="hljs-built_in">String</span>,
    last_name: <span class="hljs-built_in">String</span>,
}
</code></pre>
<p>The <code>Debug</code> derive macro will result in the following code (presentational, not exact):</p>
<pre><code class="lang-rust"><span class="hljs-keyword">impl</span> core::fmt::<span class="hljs-built_in">Debug</span> <span class="hljs-keyword">for</span> User {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">fmt</span></span>(&amp;<span class="hljs-keyword">self</span>, f: &amp;<span class="hljs-keyword">mut</span> core::fmt::Formatter) -&gt; core::fmt::<span class="hljs-built_in">Result</span> {
        f.debug_struct(
            <span class="hljs-string">"User"</span>
        )
        .field(<span class="hljs-string">"username"</span>, &amp;<span class="hljs-keyword">self</span>.username)
        .field(<span class="hljs-string">"first_name"</span>, &amp;<span class="hljs-keyword">self</span>.first_name)
        .field(<span class="hljs-string">"last_name"</span>, &amp;<span class="hljs-keyword">self</span>.last_name)
        .finish()
    }
}
</code></pre>
<p>As you might be able to tell, nobody wants to write this code for all of their custom structs and enums again and again. This simple macro gives you a sense of both the power of macros in Rust, as well as why they're an essential part of the language itself.</p>
<p>During actual compilation, the same code would give the following as the result:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">User</span></span> {
    username: <span class="hljs-built_in">String</span>,
    first_name: <span class="hljs-built_in">String</span>,
    last_name: <span class="hljs-built_in">String</span>,
}

<span class="hljs-keyword">impl</span> core::fmt::<span class="hljs-built_in">Debug</span> <span class="hljs-keyword">for</span> User {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">fmt</span></span>(&amp;<span class="hljs-keyword">self</span>, f: &amp;<span class="hljs-keyword">mut</span> core::fmt::Formatter) -&gt; ::core::fmt::<span class="hljs-built_in">Result</span> {
        f.debug_struct(
            <span class="hljs-string">"User"</span>
        )
        .field(<span class="hljs-string">"username"</span>, &amp;<span class="hljs-keyword">self</span>.username)
        .field(<span class="hljs-string">"first_name"</span>, &amp;<span class="hljs-keyword">self</span>.first_name)
        .field(<span class="hljs-string">"last_name"</span>, &amp;<span class="hljs-keyword">self</span>.last_name)
        .finish()
    }
}
</code></pre>
<p>Notice how the original type declaration is preserved in the output code. This is one of the main differences between derive macros vs others. Derive macros preserve the input type without modifications. They only add additional code to the output. On the other hand, all the other macros do not behave the same way. They only preserve the target when the output for macro itself includes the target as well.</p>
<h4 id="heading-attribute-macros">Attribute macros</h4>
<p>Attribute macros, in addition to data types, are usually applied to code blocks such as functions, impl blocks, inline blocks, and so on. They're usually used to either transform the target code in some way, or annotate it with additional information. </p>
<p>The most common use case for these is to modify a function to add additional functionality or logic to it. For example, you can easily write an attribute macro that:</p>
<ul>
<li>Logs all input and output parameters</li>
<li>Logs the total runtime of the function</li>
<li>Counts the number of times that function is called</li>
<li>Adds pre-determined additional fields to any struct</li>
</ul>
<p>and so on.</p>
<p>All of the things I mentioned above, and much more, combined form the insanely popular and useful <code>instrumentation</code> macro in Rust provided by the <code>tracing</code> crate. Of course I'm massively simplifying here, but it's good enough as an example.</p>
<p>If you're used to using Clippy, it might have screamed at you a couple of times to add the <code>#[must_use]</code> attribute to your function or method. </p>
<p>That is an example of macros used to annotate the function with additional information. It tells the compiler to warn the user if the return value from this function call isn't used. The <code>Result</code> type is already annotated with <code>#[must_use]</code> by default, which is how you see the warning <code>Unused Result&lt;...&gt; that must be used</code> when you don't use a return value of <code>Result</code> type.</p>
<p>Attribute macros are also what powers <a target="_blank" href="https://doc.rust-lang.org/reference/conditional-compilation.html">conditional compilation</a> in Rust.</p>
<h4 id="heading-functional-macros">Functional macros</h4>
<p>Functional macros are macros disguised as functions. These are the least restrictive type of procedural macros, as they can be used literally anywhere, as long as they output code that's valid in the context that they're used in. </p>
<p>These macros aren't "applied" to anything unlike the 2 others, but rather called just like you'd call a function. As arguments, you can literally pass in anything you want, as long as your macro knows how to parse it. This includes everything all the way from no arguments to valid Rust code to random gibberish that only your macro can make sense of.</p>
<p>They're in a sense the procedural version of declarative macros. If you need to execute Rust code and also be able to parse custom syntax, functional macros are your go-to tool. They're also useful if you need macro-like functionality in places where other macros cannot be used.</p>
<p>After that lengthy description of the basic information regarding macros, it's finally time to dive into actually writing procedural macros.</p>
<h2 id="heading-prerequisites">Prerequisites</h2>
<p>There are certain rules around writing your own procedural-macros that you'll need to follow. These rules apply to all 3 types of procedural macros. They are:</p>
<ul>
<li>Procedural macros can only be added to a project that is marked as <code>proc-macro</code> in <code>Cargo.toml</code></li>
<li>Projects marked as such cannot export anything other than procedural macros.</li>
<li>The macros themselves have to all be declared in the <code>lib.rs</code> file.</li>
</ul>
<p>Let’s begin by setting up our project with this code:</p>
<pre><code class="lang-shell">cargo new --bin my-app
cd my-app
cargo new --lib my-app-macros;
</code></pre>
<p>This will create a root project, as well as a sub-project within it that will host our macros. You need some changes in the <code>Cargo.toml</code> files for both these projects.</p>
<p>First, the <code>Cargo.toml</code> file for <code>my-app-macros</code> should have the following contents (notice that you need to declare a lib section that has the <code>proc-macro</code> property):</p>
<pre><code class="lang-toml"><span class="hljs-comment"># my-app/my-app-macros/Cargo.toml</span>

<span class="hljs-section">[package]</span>
<span class="hljs-attr">name</span> = <span class="hljs-string">"my-app-macros"</span>
<span class="hljs-attr">version</span> = <span class="hljs-string">"0.1.0"</span>
<span class="hljs-attr">edition</span> = <span class="hljs-string">"2021"</span>

<span class="hljs-section">[lib]</span>
<span class="hljs-attr">name</span> = <span class="hljs-string">"my_app_macros"</span>
<span class="hljs-attr">path</span> = <span class="hljs-string">"src/lib.rs"</span>
<span class="hljs-attr">proc-macro</span> = <span class="hljs-literal">true</span>

<span class="hljs-section">[dependencies]</span>
</code></pre>
<p>Next, the <code>Cargo.toml</code> file for <code>my-app</code> should have the following contents:</p>
<pre><code class="lang-toml"><span class="hljs-comment"># my-app/Cargo.toml</span>

<span class="hljs-attr">workspace</span> = { members = [<span class="hljs-string">"my-app-macros"</span>] }

<span class="hljs-section">[package]</span>
<span class="hljs-attr">name</span> = <span class="hljs-string">"my-app"</span>
<span class="hljs-attr">version</span> = <span class="hljs-string">"0.1.0"</span>
<span class="hljs-attr">edition</span> = <span class="hljs-string">"2021"</span>
<span class="hljs-attr">resolver</span> = <span class="hljs-string">"2"</span>

<span class="hljs-comment"># See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html</span>

<span class="hljs-section">[dependencies]</span>
<span class="hljs-attr">my-app-macros</span> = { path = <span class="hljs-string">"./my-app-macros"</span> }
</code></pre>
<p>You need to set the dependency resolver version to “2”, and add your macros project as a dependency of the <code>my-app</code> project.</p>
<h3 id="heading-helpful-dependencies">Helpful dependencies</h3>
<p>From the compiler’s point of view, this is how macros work:</p>
<ul>
<li>They take a stream of tokens as input (and optionally a stream of tokens as arguments to the macro itself).</li>
<li>They return a stream of tokens as output.</li>
</ul>
<p>That’s all that the compiler knows! And as you'll soon see, it's enough for the compiler to know that.</p>
<p>This does create a problem though. You need to be able to make sense of this "stream of tokens" in a way where you correctly understand them, whether as Rust code or custom syntax, are able to modify them, and also output them. Doing so manually is no easy task, and for the purposes of this tutorial, it is out of scope. </p>
<p>We can, however, rely on great open source work done by many developers to ease this for us. You need to add a few dependencies to help with this problem:</p>
<ul>
<li><code>syn</code> — A syntax parser for Rust. This helps you to parse the input token stream as Rust AST. AST is a concept that you mostly run into when trying to write your own interpreter or compiler, but a basic understanding is essential for working with macros. Macros, after all, are just extensions that you write for the compiler in a sense. If you’re interested in learning more about what ASTs are, <a target="_blank" href="https://dev.to/balapriya/abstract-syntax-tree-ast-explained-in-plain-english-1h38">check out this very helpful introduction</a>.</li>
<li><code>quote</code> — quote is, and this is a huge generalisation, a crate that helps us perform the reverse operation of what <code>syn</code> does. It helps us convert Rust source code into a stream of tokens that we can output from our macro.</li>
<li><code>proc-macro2</code> — The standard library provides a <code>proc-macro</code> crate, but the types it provides cannot exist outside of procedural macros. <code>proc-macro2</code> is a wrapper around the standard library that makes all of the internal types usable outside of the context of macros. This, for example, allows both <code>syn</code> and <code>quote</code> to not only be used for procedural macros, but in regular Rust code as well, should you ever have such a need. And we will indeed be using that extensively if we ever want to unit test our macros or their expansions.</li>
<li><code>darling</code>–It facilitates parsing and working with macro arguments, which is otherwise a tedious process due to having to manually parse it from the syntax tree. <code>darling</code> provides us with <code>serde</code>-like ability to automatically parse input argument tree into our arguments struct. It also helps us in error handling around invalid arguments, required arguments, and so on.</li>
</ul>
<p>While these projects are contributed to by many developers, I want to give special thanks to <a target="_blank" href="https://crates.io/users/dtolnay">David Tolnay</a>. He's a legend in the Rust community and is the creator of most of these projects, and many many more open source crates in Rust.</p>
<p>Let’s quickly add these dependencies to our project and start writing our macro:</p>
<pre><code class="lang-shell">// my-app-macros

cargo add syn quote proc-macro2 darling
</code></pre>
<h2 id="heading-how-to-write-a-simple-derive-macro">How to Write a Simple Derive Macro</h2>
<p>You are going to learn how to write a <code>Derive</code> macro in this section. By this time, you should already be aware of the different types of macros and what they entail, as we talked about them in the above sections.</p>
<h3 id="heading-the-intostringhashmap-derive-macro">The <code>IntoStringHashMap</code> Derive Macro</h3>
<p>Let's say you have an app where you need to be able to convert structs into hash maps, that uses the <code>String</code> type for both keys and values. This means that it should work with any struct where all of the fields are convertible to <code>String</code> type using the <code>Into</code> trait.</p>
<h3 id="heading-how-to-declare-a-derive-macro">How to Declare a Derive Macro</h3>
<p>You declare macros by creating a function, and annotating that function using attribute macros that tell the compiler to consider that function as a macro declaration. Since your <code>lib.rs</code> is empty right now, you also need to declare <code>proc-macro2</code> as an extern crate:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// my-app-macros/src/lib.rs</span>
<span class="hljs-keyword">extern</span> <span class="hljs-keyword">crate</span> proc_macro;

<span class="hljs-keyword">use</span> proc_macro::TokenStream;

<span class="hljs-meta">#[proc_macro_derive(IntoStringHashMap)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">derive_into_hash_map</span></span>(item: TokenStream) -&gt; TokenStream {
    todo!()
}
</code></pre>
<p>All we’re doing here is declaring our macro as a derive macro with the identifier <code>IntoStringHashMap</code>. Note that the function name is not important here. What's important is the identifier passed to the <code>proc_macro_derive</code> attribute macro. </p>
<p>Let's immediately see how you can use this – we'll come back and finish the implementation later:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// my-app/src/main.rs</span>

<span class="hljs-keyword">use</span> my_app_macros::IntoStringHashMap;

<span class="hljs-meta">#[derive(IntoStringHashMap)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">User</span></span> {
    username: <span class="hljs-built_in">String</span>,
    first_name: <span class="hljs-built_in">String</span>,
    last_name: <span class="hljs-built_in">String</span>,
    age: <span class="hljs-built_in">u32</span>,
}

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {

}
</code></pre>
<p>You can just use your macro as any other derive macro, using the identifier you declared for it (in this case it was <code>IntoStringHashMap</code>).</p>
<p>If you try and compile your code at this stage, you should see the following compilation error:</p>
<pre><code class="lang-shell">   Compiling my-app v0.1.0 

error: proc-macro derive panicked
 --&gt; src/main.rs:3:10
  |
3 | #[derive(IntoHashMap)]
  |          ^^^^^^^^^^^
  |
  = help: message: not yet implemented

error: could not compile `my-app` (bin "my-app") due to 1 previous error
</code></pre>
<p>This clearly proves that our macro was executed during the compilation stage, as, if you're not familiar with the <code>todo!()</code> macro, panics with <code>help: message: not yet implemented</code> when executed. </p>
<p>This means that both our macro declaration and its usage works. We can move on to actually implementing this macro now.</p>
<h3 id="heading-how-to-parse-the-macros-input">How to Parse the Macro's Input</h3>
<p>First, you parse the input token stream as a <code>DeriveInput</code> using <code>syn</code>, which is a representation of any target that you can use a derive macro with<em>:</em></p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> input = syn::parse_macro_input!(item <span class="hljs-keyword">as</span> syn::DeriveInput);
</code></pre>
<p><code>syn</code> provides us with the <code>parse_macro_input</code> macro that uses a somewhat custom syntax as its arguments. You provide it the name of your input variable, the <code>as</code> keyword, and the data type in <code>syn</code> that it should parse the input token stream as (in our case, a <code>DeriveInput</code>). </p>
<p>If you jump into the source code for <code>DeriveInput</code>, you'll see that it gives us the following information:</p>
<ul>
<li><code>attrs</code>: Attributes applied to this type, whether other attribute macros declared by us, or the built-in ones such as <code>must_use</code>.</li>
<li><code>vis</code>: The visibility specifier for this type declaration.</li>
<li><code>ident</code>: The identifier (name) of the type.</li>
<li><code>generics</code>: Information about the generic parameters this type takes, including lifetimes.</li>
<li><code>data</code>: An enum that describes whether the target is a struct, an enum, or a union, and also provides us with more information for each of these.</li>
</ul>
<p>These field names and their types (apart from data field) are pretty standard across targets supported by <code>syn</code>, such as functions, enums, and so on.</p>
<p>If you further jump into the declaration of the <code>Data</code> enum, and into <code>DataStruct</code> in particular, you'll see that it provides you with a field called <code>fields</code>. This is a collection of all the fields of this struct and you can use it to iterate over them. This is exactly what we need to build our hash map!</p>
<p>The complete implementation for this macro looks like this:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// my-app/my-app-macros/lib.rs</span>

<span class="hljs-keyword">extern</span> <span class="hljs-keyword">crate</span> proc_macro2;

<span class="hljs-keyword">use</span> proc_macro::TokenStream;
<span class="hljs-keyword">use</span> quote::quote;
<span class="hljs-keyword">use</span> syn::Data;

<span class="hljs-meta">#[proc_macro_derive(IntoHashMap)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">into_hash_map</span></span>(item: TokenStream) -&gt; TokenStream {
    <span class="hljs-keyword">let</span> input = syn::parse_macro_input!(item <span class="hljs-keyword">as</span> syn::DeriveInput);

    <span class="hljs-keyword">let</span> struct_identifier = &amp;input.ident;

    <span class="hljs-keyword">match</span> &amp;input.data {
        Data::Struct(syn::DataStruct { fields, .. }) =&gt; {
            <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> implementation = quote!{
                <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> hash_map = std::collections::HashMap::&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>&gt;::new();
            };

            <span class="hljs-keyword">for</span> field <span class="hljs-keyword">in</span> fields {
                <span class="hljs-keyword">let</span> identifier = field.ident.as_ref().unwrap();
                implementation.extend(quote!{
                    hash_map.insert(<span class="hljs-built_in">stringify!</span>(#identifier).to_string(), <span class="hljs-built_in">String</span>::from(value.#identifier));
                });
            }

            quote! {
                <span class="hljs-meta">#[automatically_derived]</span>
                <span class="hljs-keyword">impl</span> <span class="hljs-built_in">From</span>&lt;#struct_identifier&gt; <span class="hljs-keyword">for</span> std::collections::HashMap&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>&gt; {
                    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">from</span></span>(value: #struct_identifier) -&gt; <span class="hljs-keyword">Self</span> {
                        #implementation

                        hash_map
                    }
                }
            }
        }
        _ =&gt; <span class="hljs-built_in">unimplemented!</span>()
    }.into()
}
</code></pre>
<p>There's a lot going on here, so let's break it down:</p>
<h3 id="heading-how-to-ensure-a-struct-target-for-the-macro">How to Ensure a <code>struct</code> Target for the Macro</h3>
<p><code>let struct_identifier = &amp;input.ident;</code>: You store the struct identifier into a separate variable, so that you can easily use it later.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">match</span> &amp;input.data {
    Data::<span class="hljs-class"><span class="hljs-keyword">struct</span>(<span class="hljs-title">syn</span></span>::DataStruct { fields, .. }) =&gt; { ... },
    _ =&gt; <span class="hljs-built_in">unimplemented!</span>()
}
</code></pre>
<p>You match over the parsed data field from <code>DeriveInput</code>. If it is of type <code>DataStruct</code> (a Rust struct) then continue, else panic, as the macro isn't implemented for other types.</p>
<h3 id="heading-how-to-build-the-output-code">How to Build the Output Code</h3>
<p>Let's take a look at the match arm implementation when you do have a <code>DataStruct</code>:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> implementation = quote!{
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> hash_map = std::collections::HashMap::&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>&gt;::new();
};
</code></pre>
<p>Here you created a new <code>TokenStream</code> using <code>quote</code>. This <code>TokenStream</code> is different than the one provided by the standard library, so don't confuse it with that. This needs to be mutable, as we'll be adding more code to this <code>TokenStream</code> soon.</p>
<p><code>TokenStream</code> is basically the inverse representation of an AST. You provide actual Rust code to the <code>quote</code> macro, and it gives us the "stream of tokens" as you've called it previously for that source code. </p>
<p>This <code>TokenStream</code> can either be converted to the macro's output type, or be manipulated using methods provided by <code>quote</code> such as extend.</p>
<p>Moving on,</p>
<pre><code class="lang-rust"><span class="hljs-keyword">for</span> field <span class="hljs-keyword">in</span> fields {
    <span class="hljs-keyword">let</span> identifier = field.ident.as_ref().unwrap();
    implementation.extend(quote!{
        hash_map.insert(
            <span class="hljs-built_in">stringify!</span>(#identifier).to_string(),
            <span class="hljs-built_in">String</span>::from(value.#identifier)
        );
    });
}
</code></pre>
<p>You loop over all of the fields. In each iteration, you first create a variable <code>identifier</code> to hold the name of the field for later use. You then use the <code>extend</code> method on our previously created <code>TokenStream</code> to add additional code to it. </p>
<p>The <code>extend</code> method just takes another <code>TokenStream</code>, which can easily be generated using <code>quote</code> macro. For the extension, you simply write code to insert a new entry into the <code>hash_map</code> that will be created in the macro output. </p>
<p>Let's have a closer look at that:</p>
<pre><code class="lang-rust">hash_map.insert(
    <span class="hljs-built_in">stringify!</span>(#identifier).to_string(),
    <span class="hljs-built_in">String</span>::from(value.#identifier)
);
</code></pre>
<p>As you know, the insert method takes a key and a value. You've told the compiler that both the key and value are of <code>String</code> type. <code>stringify</code> is a built-in macro in the standard library, that converts any <code>Ident</code> type into its <code>&amp;str</code> equivalent. You use it here to convert your field identifiers into actual <code>&amp;str</code>. You then call <code>to_string()</code> method on it to convert it to the <code>String</code> type.</p>
<p>But what does the <code>#identifier</code> represent?</p>
<p><code>quote</code> provides you with the ability to use any variables declared outside of the <code>TokenStream</code> within it using the <code>#</code> prefix. Think of it as <code>{}</code> in format args. <code>#identifier</code> in this case simply gets replaced with the field identifier we declared outside of the <code>extend</code> call. So you basically call the <code>stringify!()</code> macro on the field identifier directly.</p>
<p>Similarly, you can access the value of a field using the familiar <code>struct_variable.field_name</code> syntax, but use the identifier variable instead of the field name instead. This is what you do when you pass the value to your insert statement: <code>String::from(value.#identifier)</code>. </p>
<p>If you've looked at the code closely, you'll realise where the <code>value</code> came from, but if not, it's just what the trait implementation method uses to declare its input argument further down.</p>
<p>Once you've built your implementation using the for loop for each field in the struct, you have a <code>TokenStream</code> which, for representational purposes, contains the following code:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> hash_map = std::collections::HashMap::&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>&gt;::new();
hash_map.insert(<span class="hljs-string">"username"</span>.to_string(), <span class="hljs-built_in">String</span>::from(value.username));
hash_map.insert(<span class="hljs-string">"first_name"</span>.to_string(), <span class="hljs-built_in">String</span>::from(value.first_name));
hash_map.insert(<span class="hljs-string">"last_name"</span>.to_string(), <span class="hljs-built_in">String</span>::from(value.last_name));
</code></pre>
<p>Moving on to finally generating the output of our macro, you have:</p>
<pre><code class="lang-rust">quote! {
    <span class="hljs-keyword">impl</span> <span class="hljs-built_in">From</span>&lt;#struct_identifier&gt; <span class="hljs-keyword">for</span> std::collections::HashMap&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>&gt; {
        <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">from</span></span>(value: #struct_identifier) -&gt; <span class="hljs-keyword">Self</span> {
            #implementation

            hash_map
        }
    }
}
</code></pre>
<p>Here you start by creating another <code>TokenStream</code> using <code>quote</code>. You write your <code>From</code> trait implementation in this block.</p>
<p>The following line again uses the <code>#</code> prefix syntax that we just looked at to declare that the trait implementation should be for your target struct, based on the identifier for the struct. In this case, this identifier will be replaced with <code>User</code> if you apply the derive macro to <code>User</code> struct.</p>
<pre><code class="lang-rust"><span class="hljs-keyword">impl</span> <span class="hljs-built_in">From</span>&lt;#struct_identifier&gt; <span class="hljs-keyword">for</span> std::collections::HashMap&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>&gt; {}
</code></pre>
<p>Finally, you have the actual method body:</p>
<pre><code class="lang-rust"><span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">from</span></span>(value: #struct_identifier) -&gt; <span class="hljs-keyword">Self</span> {
    #implementation

    hash_map
}
</code></pre>
<p>As you can see, you can easily nest <code>TokenStream</code>s into other <code>TokenStreams</code> using the same <code>#</code> syntax that lets you use external variables within the <code>quote</code> macro. </p>
<p>Here, you declare that your hash map implementation should be inserted as the first few lines of the function. And then you simply return the same <code>hash_map</code>. This completes your trait implementation.</p>
<p>As the very last step, you call <code>.into()</code> on the return type of our <code>match</code> block, which returns the output of <code>quote</code> macro call. This converts the <code>TokenStream</code> type used by <code>quote</code> into the <code>TokenStream</code> type that comes from the standard library and is expected by the compiler to be returned from a macro.</p>
<p>If it was harder to understand it when I broke it down line by line, you can look at the following complete but commented code in addition:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Tell the compiler that this function is a derive macro, and the identifier for derive is `IntoHashMap`.</span>
<span class="hljs-meta">#[proc_macro_derive(IntoHashMap)]</span>
<span class="hljs-comment">// Declare a function that takes an input `TokenStream` and outputs `TokenStream`.</span>
<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">into_hash_map</span></span>(item: TokenStream) -&gt; TokenStream {
    <span class="hljs-comment">// Parse the input token stream as `DeriveInput` type provided by `syn` crate.</span>
    <span class="hljs-keyword">let</span> input = syn::parse_macro_input!(item <span class="hljs-keyword">as</span> syn::DeriveInput);

    <span class="hljs-comment">// Store the struct identifier (name) into a variable so that you can insert it in the output code.</span>
    <span class="hljs-keyword">let</span> struct_identifier = &amp;input.ident;

    <span class="hljs-comment">// Match over the target type to which the derive macro was applied</span>
    <span class="hljs-keyword">match</span> &amp;input.data {
        <span class="hljs-comment">// Match that the target was a struct, and destructure the `fields` field from its information.</span>
        Data::Struct(syn::DataStruct { fields, .. }) =&gt; {
            <span class="hljs-comment">// Declare a new quote block that will hold the code for your implementation of the hash map.</span>
            <span class="hljs-comment">// This block will both create a new hash map, and also populate it with all of the fields from</span>
            <span class="hljs-comment">// the struct.</span>
            <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> implementation = quote!{
                <span class="hljs-comment">// This is just code that you want to see in the output. In this case, you want to have</span>
                <span class="hljs-comment">// a new hash map created.</span>
                <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> hash_map = std::collections::HashMap::&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>&gt;::new();
            };

            <span class="hljs-comment">// Iterate over all the fields of your target struct</span>
            <span class="hljs-keyword">for</span> field <span class="hljs-keyword">in</span> fields {
                <span class="hljs-comment">// Create a variable to store the identifier (name) of the field for later use</span>
                <span class="hljs-keyword">let</span> identifier = field.ident.as_ref().unwrap();
                <span class="hljs-comment">// Extend your `implementation` block to include code in the output that populates</span>
                <span class="hljs-comment">// the hash map you create with the information from current field.</span>
                implementation.extend(quote!{
                    <span class="hljs-comment">// Convert the field identifier to a string using `stringify!` macro. This is used</span>
                    <span class="hljs-comment">// as the key in your new hash map entry. For value of this key, we access the field value</span>
                    <span class="hljs-comment">// from the struct using `value.#identifier`, where `#identifier` is replaced with the actual</span>
                    <span class="hljs-comment">// field name in output code.</span>
                    hash_map.insert(<span class="hljs-built_in">stringify!</span>(#identifier).to_string(), <span class="hljs-built_in">String</span>::from(value.#identifier));
                });
            }

            <span class="hljs-comment">// Create the final output block</span>
            quote! {
                <span class="hljs-comment">// Implement the `From` trait to allow converting your target struct, identified by</span>
                <span class="hljs-comment">// `struct_identifier` to a HashMap with both the key and the value as `String`.</span>
                <span class="hljs-comment">// Just like previously, #struct_identifier is replaced with the actual name of the</span>
                <span class="hljs-comment">// target struct in output code.</span>
                <span class="hljs-keyword">impl</span> <span class="hljs-built_in">From</span>&lt;#struct_identifier&gt; <span class="hljs-keyword">for</span> std::collections::HashMap&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>&gt; {
                    <span class="hljs-comment">// This is just a method that the `From` trait requires you to implement. The</span>
                    <span class="hljs-comment">// type of the input value is again `#struct_identifier`, which is replaced with</span>
                    <span class="hljs-comment">// the name of the target struct in output code.</span>
                    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">from</span></span>(value: #struct_identifier) -&gt; <span class="hljs-keyword">Self</span> {
                        <span class="hljs-comment">// Include the `implementation` block you created using `quote!` as the body</span>
                        <span class="hljs-comment">// of this method. `quote` allows you to nest other `quote` blocks freely.</span>
                        #implementation

                        <span class="hljs-comment">// Return the hash_map.</span>
                        hash_map
                    }
                }
            }
        }
        <span class="hljs-comment">// If the target is of any other type, panic.</span>
        _ =&gt; <span class="hljs-built_in">unimplemented!</span>()
        <span class="hljs-comment">// Convert the `TokenStream` type used by `quote` to `TokenStream` type used by the</span>
        <span class="hljs-comment">// standard library and the compiler</span>
    }.into()
}
</code></pre>
<p>And that's it. You've written your very first procedural macro in Rust!</p>
<p><strong>It's now time to enjoy the fruits of your labour.</strong></p>
<h3 id="heading-how-to-use-your-derive-macro">How to Use Your Derive Macro</h3>
<p>Coming back to your <code>my-app/main.rs</code>, let's debug-print the hashmap that you create using the macro you implemented. Your <code>main.rs</code> should look like this:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// my-app/src/main.rs</span>

<span class="hljs-keyword">use</span> std::collections::HashMap;
<span class="hljs-keyword">use</span> my_app_macros::IntoHashMap;

<span class="hljs-meta">#[derive(IntoHashMap)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">User</span></span> {
    username: <span class="hljs-built_in">String</span>,
    first_name: <span class="hljs-built_in">String</span>,
    last_name: <span class="hljs-built_in">String</span>,
}

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> user = User {
        username: <span class="hljs-string">"username"</span>.to_string(),
        first_name: <span class="hljs-string">"First"</span>.to_string(),
        last_name: <span class="hljs-string">"Last"</span>.to_string(),
    };

    <span class="hljs-keyword">let</span> hash_map = HashMap::&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>&gt;::from(user);

    dbg!(hash_map);
}
</code></pre>
<p>If you run this using <code>cargo run</code>, you should see the following output in your terminal:</p>
<pre><code class="lang-shell">[src/main.rs:20:5] hash_map = {
    "last_name": "Last",
    "first_name": "First",
    "username": "username",
}
</code></pre>
<p>And there you go!</p>
<h3 id="heading-how-to-improve-our-implementation">How to Improve Our Implementation</h3>
<p>There is a better way to work with iterators and <code>quote</code> that I skipped over in our original implementation – intentionally so, because it requires us to learn a bit more of the syntax specific to <code>quote</code>. </p>
<p>Let's see what it would have looked like with that, before we dive into how it works:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> input = syn::parse_macro_input!(item <span class="hljs-keyword">as</span> syn::DeriveInput);
    <span class="hljs-keyword">let</span> struct_identifier = &amp;input.ident;

    <span class="hljs-keyword">match</span> &amp;input.data {
        Data::Struct(syn::DataStruct { fields, .. }) =&gt; {
            <span class="hljs-keyword">let</span> field_identifiers = fields.iter().map(|item| item.ident.as_ref().unwrap()).collect::&lt;<span class="hljs-built_in">Vec</span>&lt;_&gt;&gt;();

            quote! {
                <span class="hljs-keyword">impl</span> <span class="hljs-built_in">From</span>&lt;#struct_identifier&gt; <span class="hljs-keyword">for</span> std::collections::HashMap&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>&gt; {
                    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">from</span></span>(value: #struct_identifier) -&gt; <span class="hljs-keyword">Self</span> {
                        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> hash_map = std::collections::HashMap::&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>&gt;::new();

                        #(
                            hash_map.insert(<span class="hljs-built_in">stringify!</span>(#field_identifiers).to_string(), <span class="hljs-built_in">String</span>::from(value.#field_identifiers));
                        )*

                        hash_map
                    }
                }
            }
        }
        _ =&gt; <span class="hljs-built_in">unimplemented!</span>()
    }.into()
</code></pre>
<p>That looks so much more concise and easier to understand! Let's look at the special bit of syntax that makes it possible – in particular, the following line:</p>
<pre><code class="lang-rust">#(
    hash_map.insert(<span class="hljs-built_in">stringify!</span>(#field_identifiers).to_string(), <span class="hljs-built_in">String</span>::from(value.#field_identifiers));
)*
</code></pre>
<p>Let's break it down. First, you wrap the entire block in a <code>#()*</code> and your code goes inside the parentheses. This syntax is what allows you to make use of any iterator inside of the parenthesis, and it will repeat that block of code for all items in the iterator, while replacing the variable with correct item in each iteration.</p>
<p>In this case, you first create a <code>field_identifiers</code> iterator, that is a collection of all the field identifiers in your target struct. You then write your <code>hash_map</code> insert statement while using the iterator directly as if it is a single item. The <code>#()*</code> wrapper converts this into the expected output of multiple lines, one for each item in the iterator.</p>
<h2 id="heading-a-more-elaborate-derive-macro">A More Elaborate Derive Macro</h2>
<p>Now that you're comfortable writing a simple Derive macro, it's time to move on and create something that will actually be useful in the real world – especially if you're working with database models.</p>
<h3 id="heading-the-derivecustommodel-macro">The <code>DeriveCustomModel</code> Macro</h3>
<p>You're going to be building a Derive macro that helps you generate derived structs from your original struct. You're going to be needing this all the time whenever you're working with databases, and only want to load part of the data.</p>
<p>For example, if you have a <code>User</code> struct, which has all of the user information, but you only want to load the name information for the User from the database, you'll need a struct that only contains those fields – unless you want to make all the fields an Option, which isn't the best idea.</p>
<p>We will also need to add an implementation of <code>From</code> trait to automatically convert from <code>User</code> struct to the derived struct. Another thing our macro needs is to be able to derive multiple models from the same target struct.</p>
<p>Let's start by declaring it in <code>lib.rs</code>:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// lib.rs</span>

<span class="hljs-meta">#[proc_macro_derive(DeriveCustomModel, attributes(custom_model))]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">derive_custom_model</span></span>(item: TokenStream) -&gt; TokenStream {
    todo!()
}
</code></pre>
<p>Most of this syntax should familiar to you by now from our previous example. The only addition we have here, is now we also define <code>attributes(custom_model)</code> in the call to <code>proc_macro_derive</code>, which basically tells the compiler to treat any attribute that begins with <code>#[custom_model]</code> as an argument for this derive macro on that target.</p>
<p>For example, once you've defined this, you can apply <code>#[custom_model(name = "SomeName")]</code> to the target struct, to define that the derived struct should have the name "SomeName". You need to parse this yourself and handle it too, of course – the definition was only to tell the compiler to pass that through to your macro implementation and not treat it as an unknown attribute.</p>
<p>Let's also create a new file that will contain the implementation detail of this macro. The macro rule states that it needs to be <strong>defined</strong> in <code>lib.rs</code>, and we've done that. The implementation itself can live anywhere in the project.</p>
<p>Create a new file <code>custom_model.rs</code>:</p>
<pre><code class="lang-shell">touch src/custom_model.rs
</code></pre>
<h3 id="heading-how-to-separate-the-implementation-from-the-declaration">How to Separate the Implementation from the Declaration</h3>
<p>Define a function that implements the <code>DeriveCustomModel</code> macro. We're also going to add all imports right away to avoid confusion later:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// custom_model.rs</span>

<span class="hljs-keyword">use</span> syn::{
    parse_macro_input, Data::Struct, DataStruct, DeriveInput, Field, Fields, Ident, Path,
};
<span class="hljs-keyword">use</span> darling::util::PathList;
<span class="hljs-keyword">use</span> darling::{FromAttributes, FromDeriveInput, FromMeta};
<span class="hljs-keyword">use</span> proc_macro::TokenStream;
<span class="hljs-keyword">use</span> quote::{quote, ToTokens};

<span class="hljs-keyword">pub</span>(<span class="hljs-keyword">crate</span>) <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">derive_custom_model_impl</span></span>(input: TokenStream) -&gt; TokenStream {
    <span class="hljs-comment">// Parse input token stream as `DeriveInput`</span>
    <span class="hljs-keyword">let</span> original_struct = parse_macro_input!(input <span class="hljs-keyword">as</span> DeriveInput);

    <span class="hljs-comment">// Destructure data &amp; ident fields from the input</span>
    <span class="hljs-keyword">let</span> DeriveInput { data, ident, .. } = original_struct.clone();
}
</code></pre>
<p>This is just a Rust function, so there are no special rules here. You can call this from the declaration just like a regular Rust function.</p>
<pre><code class="lang-rust"><span class="hljs-meta">#[proc_macro_derive(DeriveCustomModel, attributes(custom_model))]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">derive_custom_model</span></span>(item: TokenStream) -&gt; TokenStream {
    custom_model::custom_model_impl(item)
}
</code></pre>
<h3 id="heading-how-to-parse-derive-macro-arguments">How to Parse Derive Macro Arguments</h3>
<p>To parse the arguments to our derive macro (which are usually provided using attributes applied to either the target or to its fields), we are going to rely on the <code>darling</code> crate to make it as simple as defining the data type for them.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// custom_model.rs</span>

<span class="hljs-comment">// Derive `FromDeriveInput` for this struct, which is a</span>
<span class="hljs-comment">// macro provided by darling to automatically add the ability</span>
<span class="hljs-comment">// to parse argument tokens into the given struct.</span>
<span class="hljs-meta">#[derive(FromDeriveInput, Clone)]</span>
<span class="hljs-comment">// We tell darling that we're looking for arguments</span>
<span class="hljs-comment">// that are defined using the `custom_model` attribute, and</span>
<span class="hljs-comment">// that we only support named structs for this.</span>
<span class="hljs-meta">#[darling(attributes(custom_model), supports(struct_named))]</span>
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">CustomModelArgs</span></span> {
    <span class="hljs-comment">// Specify parameters for generating a derive model.</span>
    <span class="hljs-comment">// Multiple models can be generated by repeating</span>
    <span class="hljs-comment">// this attribute with parameters for each model.</span>
    <span class="hljs-meta">#[darling(default, multiple, rename = <span class="hljs-meta-string">"model"</span>)]</span>
    <span class="hljs-keyword">pub</span> models: <span class="hljs-built_in">Vec</span>&lt;CustomModel&gt;,
}
</code></pre>
<p>We've told <code>darling</code> that for arguments to the struct, we should expect a list of <code>model</code> arguments, and each one will define parameters for a single derived model. This allows us to use the macro to generate multiple derived structs from a single input struct.</p>
<p>Next, let's define the arguments for each model:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// custom_model.rs</span>

<span class="hljs-comment">// Derive `FromMeta` for this struct, which is a</span>
<span class="hljs-comment">// macro provided by darling to automatically add the ability</span>
<span class="hljs-comment">// to parse metadata into the given struct.</span>
<span class="hljs-meta">#[derive(FromMeta, Clone)]</span>
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">CustomModel</span></span> {
    <span class="hljs-comment">// Name of the generated model.</span>
    name: <span class="hljs-built_in">String</span>,
    <span class="hljs-comment">// Comma-separated list of field identifiers</span>
    <span class="hljs-comment">// to be included in the generated model</span>
    fields: PathList,
    <span class="hljs-comment">// List of additional derives to apply to the</span>
    <span class="hljs-comment">// resulting struct such as `Eq` or `Hash`.</span>
    <span class="hljs-meta">#[darling(default)]</span>
    extra_derives: PathList,
}
</code></pre>
<p>In this, we have two required arguments, <code>name</code> and <code>fields</code>, and one optional argument <code>extra_derives</code>. It's optional because of the <code>#[darling(default)]</code> annotation on it.</p>
<h3 id="heading-how-to-implement-derivecustommodel">How to Implement <code>DeriveCustomModel</code></h3>
<p>Now that we have all of our data types defined, let's get to parsing – which is as simple as calling a method on our argument struct! The complete function implementation should like this:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// custom_model.rs</span>

<span class="hljs-keyword">pub</span>(<span class="hljs-keyword">crate</span>) <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">derive_custom_model_impl</span></span>(input: TokenStream) -&gt; TokenStream {
    <span class="hljs-comment">// Parse input token stream as `DeriveInput`</span>
    <span class="hljs-keyword">let</span> original_struct = parse_macro_input!(input <span class="hljs-keyword">as</span> DeriveInput);

    <span class="hljs-comment">// Destructure data &amp; ident fields from the input</span>
    <span class="hljs-keyword">let</span> DeriveInput { data, ident, .. } = original_struct.clone();

    <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> Struct(data_struct) = data {
        <span class="hljs-comment">// Extract the fields from this data struct</span>
        <span class="hljs-keyword">let</span> DataStruct { fields, .. } = data_struct;

        <span class="hljs-comment">// `darling` provides this method on the struct</span>
        <span class="hljs-comment">// to easily parse arguments, and also handles</span>
        <span class="hljs-comment">// errors for us.</span>
        <span class="hljs-keyword">let</span> args = <span class="hljs-keyword">match</span> CustomModelArgs::from_derive_input(&amp;original_struct) {
            <span class="hljs-literal">Ok</span>(v) =&gt; v,
            <span class="hljs-literal">Err</span>(e) =&gt; {
                <span class="hljs-comment">// If darling returned an error, generate a</span>
                <span class="hljs-comment">// token stream from it so that the compiler</span>
                <span class="hljs-comment">// shows the error in the right location.</span>
                <span class="hljs-keyword">return</span> TokenStream::from(e.write_errors());
            }
        };

        <span class="hljs-comment">// Destructure `models` field from parsed args.</span>
        <span class="hljs-keyword">let</span> CustomModelArgs { models } = args;

        <span class="hljs-comment">// Create a new output</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> output = quote!();

        <span class="hljs-comment">// Panic if no models are defined but macro is</span>
        <span class="hljs-comment">// used.</span>
        <span class="hljs-keyword">if</span> models.is_empty() {
            <span class="hljs-built_in">panic!</span>(
                <span class="hljs-string">"Please specify at least 1 model using the `model` attribute"</span>
            )
        }

        <span class="hljs-comment">// Iterate over all defined models</span>
        <span class="hljs-keyword">for</span> model <span class="hljs-keyword">in</span> models {
            <span class="hljs-comment">// Generate custom model from target struct's fields and `model` args.</span>
            <span class="hljs-keyword">let</span> generated_model = generate_custom_model(&amp;fields, &amp;model);

            <span class="hljs-comment">// Extend the output to include the generated model</span>
            output.extend(quote!(#generated_model));
        }

        <span class="hljs-comment">// Convert output into TokenStream and return</span>
        output.into()
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-comment">// Panic if target is not a named struct</span>
        <span class="hljs-built_in">panic!</span>(<span class="hljs-string">"DeriveCustomModel can only be used with named structs"</span>)
    }
}
</code></pre>
<p>The code that generates tokens for each model has been extracted away to another function that we call <code>generate_custom_model</code>. Let's implement that as well:</p>
<h3 id="heading-how-to-generate-each-custom-model">How to Generate Each Custom Model</h3>
<pre><code class="lang-rust"><span class="hljs-comment">// custom_model.rs</span>

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">generate_custom_model</span></span>(fields: &amp;Fields, model: &amp;CustomModel) -&gt; proc_macro2::TokenStream {
    <span class="hljs-keyword">let</span> CustomModel {
        name,
        fields: target_fields,
        extra_derives,
    } = model;

    <span class="hljs-comment">// Create new fields output</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> new_fields = quote!();

    <span class="hljs-comment">// Iterate over all fields in the source struct</span>
    <span class="hljs-keyword">for</span> Field {
        <span class="hljs-comment">// The identifier for this field</span>
        ident,
        <span class="hljs-comment">// Any attributes applied to this field</span>
        attrs,
        <span class="hljs-comment">// The visibility specifier for this field</span>
        vis,
        <span class="hljs-comment">// The colon token `:`</span>
        colon_token,
        <span class="hljs-comment">// The type of this field</span>
        ty,
        ..
    } <span class="hljs-keyword">in</span> fields
    {
        <span class="hljs-comment">// Make sure that field has an identifier, panic otherwise</span>
        <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(ident) = ident <span class="hljs-keyword">else</span> {
            <span class="hljs-built_in">panic!</span>(<span class="hljs-string">"Failed to get struct field identifier"</span>)
        };

        <span class="hljs-comment">// Try to convert field identifier to `Path` which is a type provided</span>
        <span class="hljs-comment">// by `syn`. We do this because `darling`'s PathList type is just a</span>
        <span class="hljs-comment">// collection of this type with additional methods on it.</span>
        <span class="hljs-keyword">let</span> path = <span class="hljs-keyword">match</span> Path::from_string(&amp;ident.clone().to_string()) {
            <span class="hljs-literal">Ok</span>(path) =&gt; path,
            <span class="hljs-literal">Err</span>(error) =&gt; <span class="hljs-built_in">panic!</span>(<span class="hljs-string">"Failed to convert field identifier to path: {error:?}"</span>),
        };

        <span class="hljs-comment">// If the list of target fields doesn't contain this field,</span>
        <span class="hljs-comment">// skip to the next field</span>
        <span class="hljs-keyword">if</span> !target_fields.contains(&amp;path) {
            <span class="hljs-keyword">continue</span>;
        }

        <span class="hljs-comment">// If it does contain it, reconstruct the field declaration</span>
        <span class="hljs-comment">// and add it in `new_fields` output so that we can use it</span>
        <span class="hljs-comment">// in the output struct.</span>
        new_fields.extend(quote! {
            #(#attrs)*
            #vis #ident #colon_token #ty,
        });
    }

    <span class="hljs-comment">// Create a new identifier for output struct</span>
    <span class="hljs-comment">// from the name provided.</span>
    <span class="hljs-keyword">let</span> struct_ident = <span class="hljs-keyword">match</span> Ident::from_string(name) {
        <span class="hljs-literal">Ok</span>(ident) =&gt; ident,
        <span class="hljs-literal">Err</span>(error) =&gt; <span class="hljs-built_in">panic!</span>(<span class="hljs-string">"{error:?}"</span>),
    };

    <span class="hljs-comment">// Create a TokenStream to hold the extra derive declarations</span>
    <span class="hljs-comment">// on new struct.</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> extra_derives_output = quote!();

    <span class="hljs-comment">// If extra_derives is not empty,</span>
    <span class="hljs-keyword">if</span> !extra_derives.is_empty() {
        <span class="hljs-comment">// This syntax is a bit compact, but you should already</span>
        <span class="hljs-comment">// know everything you need to understand it by now.</span>
        extra_derives_output.extend(quote! {
            #(#extra_derives,)*
        })
    }

    <span class="hljs-comment">// Construct the final struct by combining all the</span>
    <span class="hljs-comment">// TokenStreams generated so far.</span>
    quote! {
        <span class="hljs-meta">#[derive(#extra_derives_output)]</span>
        <span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> #<span class="hljs-title">struct_ident</span></span> {
            #new_fields
        }
    }
}
</code></pre>
<h3 id="heading-how-to-use-your-derivecustommodel-macro">How to Use Your <code>DeriveCustomModel</code> Macro</h3>
<p>Coming back to your <code>my-app/main.rs</code>, let's debug-print the generated hash-maps for your new structs that you create using the macro you implemented. Your <code>main.rs</code> should look like this:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// my-app/src/main.rs</span>

<span class="hljs-keyword">use</span> macros::{DeriveCustomModel, IntoStringHashMap};
<span class="hljs-keyword">use</span> std::collections::HashMap;

<span class="hljs-meta">#[derive(DeriveCustomModel)]</span>
<span class="hljs-meta">#[custom_model(model(
    name = <span class="hljs-meta-string">"UserName"</span>,
    fields(first_name, last_name),
    extra_derives(IntoStringHashMap)
))]</span>
<span class="hljs-meta">#[custom_model(model(name = <span class="hljs-meta-string">"UserInfo"</span>, fields(username, age), extra_derives(Debug)))]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">User2</span></span> {
    username: <span class="hljs-built_in">String</span>,
    first_name: <span class="hljs-built_in">String</span>,
    last_name: <span class="hljs-built_in">String</span>,
    age: <span class="hljs-built_in">u32</span>,
}

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-keyword">let</span> user_name = UserName {
        first_name: <span class="hljs-string">"first_name"</span>.to_string(),
        last_name: <span class="hljs-string">"last_name"</span>.to_string(),
    };
    <span class="hljs-keyword">let</span> hash_map = HashMap::&lt;<span class="hljs-built_in">String</span>, <span class="hljs-built_in">String</span>&gt;::from(user_name);

    dbg!(hash_map);

    <span class="hljs-keyword">let</span> user_info = UserInfo {
        username: <span class="hljs-string">"username"</span>.to_string(),
        age: <span class="hljs-number">27</span>,
    };

    dbg!(user_info);
}
</code></pre>
<p>As you can see, <code>extra_derives</code> is already useful to us since we need to derive <code>Debug</code> and <code>IntoStringHashMap</code> for the new models.</p>
<p>If you run this using <code>cargo run</code>, you should see the following output in your terminal:</p>
<pre><code class="lang-shell">[src/main.rs:32:5] hash_map = {
    "last_name": "last_name",
    "first_name": "first_name",
}
[src/main.rs:39:5] user_info = UserInfo {
    username: "username",
    age: 27,
}
</code></pre>
<p>We are going to wrap up the derive macros here.</p>
<h2 id="heading-a-simple-attribute-macro">A Simple Attribute Macro</h2>
<p>In this section, you're going to learn how to write an <strong>attribute</strong> macro.</p>
<h3 id="heading-the-logduration-attribute">The <code>log_duration</code> Attribute</h3>
<p>You are going to write a simple attribute macro that can be applied to any function (or method) that will log the total run time of that function each time the function is called.</p>
<h3 id="heading-how-to-declare-an-attribute-macro">How to Declare an Attribute Macro</h3>
<p>You declare attribute macros by creating a function and annotating that function using the <code>proc_macro_attribute</code> macro that tells the compiler to consider that function as a macro declaration. Let's see what that looks like:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// my-app-macros/src/lib.rs</span>

<span class="hljs-meta">#[proc_macro_attribute]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">log_duration</span></span>(args: TokenStream, item: TokenStream) -&gt; TokenStream {
    log_duration_impl(args, item)
}
</code></pre>
<p>For these macros, the function name is important, as that also becomes the name of the macro. As you can see, these take two different arguments. The first is the argument passed to the attribute macro, and the second is the target of the attribute macro.</p>
<p>Let's also implement <code>log_duration_impl</code>. Create a new file <code>log_duration.rs</code>:</p>
<pre><code class="lang-shell">touch src/log_duration.rs
</code></pre>
<h3 id="heading-how-to-implement-the-logduration-attribute-macro">How to Implement the <code>log_duration</code> Attribute Macro</h3>
<p>I'm going to give you the complete implementation first, and then I'll break down the parts that I haven't used so far:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// my-app-macros/src/log_duration.rs</span>

<span class="hljs-keyword">use</span> proc_macro::TokenStream;
<span class="hljs-keyword">use</span> quote::quote;
<span class="hljs-keyword">use</span> syn::{parse_macro_input, ItemFn};

<span class="hljs-keyword">pub</span>(<span class="hljs-keyword">crate</span>) <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">log_duration_impl</span></span>(_args: TokenStream, input: TokenStream) -&gt; TokenStream {
    <span class="hljs-comment">// Parse the input as `ItemFn` which is a type provided</span>
    <span class="hljs-comment">// by `syn` to represent a function.</span>
    <span class="hljs-keyword">let</span> input = parse_macro_input!(input <span class="hljs-keyword">as</span> ItemFn);

    <span class="hljs-keyword">let</span> ItemFn {
        <span class="hljs-comment">// The function signature</span>
        sig,
        <span class="hljs-comment">// The visibility specifier of this function</span>
        vis,
        <span class="hljs-comment">// The function block or body</span>
        block,
        <span class="hljs-comment">// Other attributes applied to this function</span>
        attrs,
    } = input;

    <span class="hljs-comment">// Extract statements in the body of the functions</span>
    <span class="hljs-keyword">let</span> statements = block.stmts;

    <span class="hljs-comment">// Store the function identifier for logging</span>
    <span class="hljs-keyword">let</span> function_identifier = sig.ident.clone();

    <span class="hljs-comment">// Reconstruct the function as output using parsed input</span>
    quote!(
        <span class="hljs-comment">// Reapply all the other attributes on this function.</span>
        <span class="hljs-comment">// The compiler doesn't include the macro we are</span>
        <span class="hljs-comment">// currently working in this list.</span>
        #(#attrs)*
        <span class="hljs-comment">// Reconstruct the function declaration</span>
        #vis #sig {
            <span class="hljs-comment">// At the beginning of the function, create an instance of `Instant`</span>
            <span class="hljs-keyword">let</span> __start = std::time::Instant::now();

            <span class="hljs-comment">// Create a new block, the body of which is the body of the function.</span>
            <span class="hljs-comment">// Store the return value of this block as a variable so that we can</span>
            <span class="hljs-comment">// return it later from the parent function.</span>
            <span class="hljs-keyword">let</span> __result = {
                #(#statements)*
            };

            <span class="hljs-comment">// Log the duration information for this function</span>
            <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{} took {}μs"</span>, <span class="hljs-built_in">stringify!</span>(#function_identifier), __start.elapsed().as_micros());

            <span class="hljs-comment">// Return the result (if any)</span>
            <span class="hljs-keyword">return</span> __result;
        }
    )
    .into()
}
</code></pre>
<p>The only things that you might not have seen previously are the <code>sig</code> and the <code>block</code> fields you get from parsing the input as <code>ItemFn</code>. <code>sig</code> contains the entire signature of a function while <code>block</code> contains the entire body of the function. This is why, by using the following code, we can basically reconstruct the unmodified function:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Example code to reconstruct unmodified fn in macro</span>

#vis #sig #block
</code></pre>
<p>In this example, you want to modify the function body, which is why you create a new block that encapsulates the original function block.</p>
<h3 id="heading-how-to-use-your-logduration-macro">How to Use Your <code>log_duration</code> Macro</h3>
<p>Coming back to <code>main.rs</code>, using an attribute macro is simpler than you might think:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// main.rs</span>

<span class="hljs-meta">#[log_duration]</span>
<span class="hljs-meta">#[must_use]</span>
<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">function_to_benchmark</span></span>() -&gt; <span class="hljs-built_in">u16</span> {
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> counter = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">for</span> _ <span class="hljs-keyword">in</span> <span class="hljs-number">0</span>..<span class="hljs-built_in">u16</span>::MAX {
        counter += <span class="hljs-number">1</span>;
    }

    counter
}

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"{}"</span>, function_to_benchmark());
}
</code></pre>
<p>When you run this, you should get the following output:</p>
<pre><code class="lang-shell">function_to_benchmark took 498μs
65535
</code></pre>
<p>We are now ready to move on to a more complex use-case.</p>
<h2 id="heading-a-more-elaborate-attribute-macro">A More Elaborate Attribute Macro</h2>
<h3 id="heading-the-cachedfn-attribute">The <code>cached_fn</code> Attribute</h3>
<p>You are going to write an attribute macro that will allow you to add caching capability to any function. For the purposes of this example, we're going to assume that our function always has <code>String</code> arguments and also returns a <code>String</code> value. </p>
<p>Some of you might know this better as a "memoized" function. </p>
<p>In addition, you will need to allow the user of this macro to tell the macro how it can generate a dynamic key based on function args.</p>
<p>To help us facilitate the caching part so that we don't get diverted, we're going to use a dependency called <code>cacache</code>. <code>cacache</code> is a Rust library for managing local key and content caches. It works by writing the cache to the disk.</p>
<p>Let's add it to the project by editing the <code>Cargo.toml</code> file for <code>my-app</code> directly:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// Cargo.toml</span>

workspace = { members = [<span class="hljs-string">"my-app-macros"</span>] }

[package]
name = <span class="hljs-string">"my-app"</span>
version = <span class="hljs-string">"0.1.0"</span>
edition = <span class="hljs-string">"2021"</span>
resolver = <span class="hljs-string">"2"</span>

# See more keys and their definitions at https:<span class="hljs-comment">//doc.rust-lang.org/cargo/reference/manifest.html</span>

[dependencies]
# New dependency
cacache = { version = <span class="hljs-string">"13.0.0"</span>, default-features = <span class="hljs-literal">false</span>, features = [<span class="hljs-string">"mmap"</span>] }
macros = { path = <span class="hljs-string">"./macros"</span> }
</code></pre>
<h3 id="heading-how-to-implement-the-cachedfn-attribute-macro">How to Implement the <code>cached_fn</code> Attribute Macro</h3>
<p>Let's start by declaring this macro in <code>lib.rs</code>:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// my-app-macros/src/lib.rs</span>

<span class="hljs-meta">#[proc_macro_attribute]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">cached_fn</span></span>(args: TokenStream, item: TokenStream) -&gt; TokenStream {
    cached_fn_impl(args, item)
}
</code></pre>
<p>Create a new file <code>cached_fn.rs</code> to store the implementation:</p>
<pre><code class="lang-shell">touch my-app-macros/src/cached_fn.rs
</code></pre>
<p>Let's define how our arguments should look before we go ahead and implement anything:</p>
<h3 id="heading-cachedfn-attribute-arguments"><code>cached_fn</code> Attribute Arguments</h3>
<pre><code class="lang-rust"><span class="hljs-comment">// my-app-macros/src/cached_fn.rs</span>

<span class="hljs-meta">#[derive(FromMeta)]</span>
<span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">CachedParams</span></span> {
    <span class="hljs-comment">// Accept any expression that we should use to compute the</span>
    <span class="hljs-comment">// key. This can be a constant string, or some computation</span>
    <span class="hljs-comment">// based on function arguments.</span>
    keygen: <span class="hljs-built_in">Option</span>&lt;Expr&gt;,
}
</code></pre>
<p>The only argument is an optional <code>keygen</code>, which is of type <code>Expr</code>. <code>Expr</code> represents any valid <a target="_blank" href="https://doc.rust-lang.org/reference/expressions.html">Rust expression</a>, so it can be very dynamic. In this example, you'll be passing an expression that generates the key based on function arguments of the target function.</p>
<p>As always, we'll first see the entire implementation and then break down the parts that are new:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// my-app-macros/src/cached_fn.rs</span>

<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">cached_fn_impl</span></span>(args: TokenStream, item: TokenStream) -&gt; TokenStream {
    <span class="hljs-comment">// Parse argument tokens as a list of NestedMeta items</span>
    <span class="hljs-keyword">let</span> attr_args = <span class="hljs-keyword">match</span> NestedMeta::parse_meta_list(args.into()) {
        <span class="hljs-literal">Ok</span>(v) =&gt; v,
        <span class="hljs-literal">Err</span>(e) =&gt; {
            <span class="hljs-comment">// Write error to output token stream if there is one</span>
            <span class="hljs-keyword">return</span> proc_macro::TokenStream::from(Error::from(e).write_errors());
        }
    };

    <span class="hljs-comment">// Parse the nested meta list as our `CachedParams` struct</span>
    <span class="hljs-keyword">let</span> CachedParams { keygen } = <span class="hljs-keyword">match</span> CachedParams::from_list(&amp;attr_args) {
        <span class="hljs-literal">Ok</span>(params) =&gt; params,
        <span class="hljs-literal">Err</span>(error) =&gt; {
            <span class="hljs-comment">// Write error to output token stream if there is one</span>
            <span class="hljs-keyword">return</span> proc_macro::TokenStream::from(Error::from(error).write_errors());
        }
    };

    <span class="hljs-comment">// Parse the input target item as a function</span>
    <span class="hljs-keyword">let</span> ItemFn {
        <span class="hljs-comment">// The function signature</span>
        sig,
        <span class="hljs-comment">// The visibility specifier of this function</span>
        vis,
        <span class="hljs-comment">// The function block or body</span>
        block,
        <span class="hljs-comment">// Other attributes applied to this function</span>
        attrs,
    } = parse_macro_input!(item <span class="hljs-keyword">as</span> ItemFn);

    <span class="hljs-comment">// Generate our key statement based on given param (or lack thereof)</span>
    <span class="hljs-keyword">let</span> key_statement = <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Some</span>(keygen) = keygen {
        <span class="hljs-comment">// If the user specified a `keygen`, use that as an expression to</span>
        <span class="hljs-comment">// get the cache key.</span>
        quote! {
            <span class="hljs-keyword">let</span> __cache_key = #keygen;
        }
    } <span class="hljs-keyword">else</span> {
        <span class="hljs-comment">// If no `keygen` was provided, use the name of the function</span>
        <span class="hljs-comment">// as cache key.</span>
        <span class="hljs-keyword">let</span> fn_name = sig.ident.clone().to_string();
        quote! {
            <span class="hljs-keyword">let</span> __cache_key = #fn_name;
        }
    };

    <span class="hljs-comment">// Reconstruct the function as output using parsed input</span>
    quote!(
        <span class="hljs-comment">// Apply other attributes from the original function to the generated function</span>
        #(#attrs)*
        #vis #sig {
            <span class="hljs-comment">// Include the key_statement we generated above as the first</span>
            <span class="hljs-comment">// thing in the function body</span>
            #key_statement

            <span class="hljs-comment">// Try to read the value from cache</span>
            <span class="hljs-keyword">match</span> cacache::read_sync(<span class="hljs-string">"./__cache"</span>, __cache_key.clone()) {
                <span class="hljs-comment">// If the value exists, parse it as string and return it</span>
                <span class="hljs-literal">Ok</span>(value) =&gt; {
                    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Data is fetched from cached"</span>);
                    from_utf8(&amp;value).unwrap().to_string()
                },
                <span class="hljs-literal">Err</span>(_) =&gt; {
                    <span class="hljs-built_in">println!</span>(<span class="hljs-string">"Data is not fetched from cached"</span>);
                    <span class="hljs-comment">// Save the output of original function block into</span>
                    <span class="hljs-comment">// a variable.</span>
                    <span class="hljs-keyword">let</span> output = #block;

                    <span class="hljs-comment">// Write the output value to cache as bytes</span>
                    cacache::write_sync(<span class="hljs-string">"./__cache"</span>, __cache_key, output.as_bytes()).unwrap();

                    <span class="hljs-comment">// Return the original output</span>
                    output
                }
            }
        }
    )
    .into()
}
</code></pre>
<p>Well, turns out that you've seen everything that we used in this one before. </p>
<p>The only new thing here is the use of the <code>cacache</code> dependency, but that's also pretty straightforward. You just give the location where you want to store the cached data as the first argument to the <code>read_sync</code> and <code>write_sync</code> functions provided by <code>cacache</code>.</p>
<p>We've also added some logging to help us verify that the macro works as expected.</p>
<h3 id="heading-how-to-use-the-cachedfn-macro">How to Use the <code>cached_fn</code> Macro</h3>
<p>To make any function memoized or cached, we simply annotate it using the <code>cached_fn</code> attribute:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/main.rs</span>

<span class="hljs-meta">#[cached_fn(keygen = <span class="hljs-meta-string">"format!(\"</span>{first_name} {last_name}\<span class="hljs-meta-string">")"</span>)]</span>
<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">test_cache</span></span>(first_name: <span class="hljs-built_in">String</span>, last_name: <span class="hljs-built_in">String</span>) -&gt; <span class="hljs-built_in">String</span> {
    <span class="hljs-built_in">format!</span>(<span class="hljs-string">"{first_name} {last_name}"</span>)
}

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    test_cache(<span class="hljs-string">"John"</span>.to_string(), <span class="hljs-string">"Appleseed"</span>.to_string());
    test_cache(<span class="hljs-string">"John"</span>.to_string(), <span class="hljs-string">"Appleseed"</span>.to_string());
    test_cache(<span class="hljs-string">"John"</span>.to_string(), <span class="hljs-string">"Doe"</span>.to_string());
}
</code></pre>
<p>If you run this, you should see the following output:</p>
<pre><code class="lang-shell">Data is not fetched from cached
Data is fetched from cached
Data is not fetched from cached
</code></pre>
<p>Which clearly shows that if the function is called more than once for the same arguments, data is returned from cache. But if the arguments are different, it doesn't return the value that was cached for a different set of arguments.</p>
<p>We did make a lot of assumptions for this one which don't hold true for a real-world use case. As such, this is only for learning purposes, but depicts a real-world use case.</p>
<p>For example, I've written attribute macros to cache HTTP handler functions using <code>redis</code> for production servers. Those have a very similar implementation to this, but contains a lot of bells and whistles to work with that particular use case.</p>
<h2 id="heading-a-simple-function-like-macro">A Simple Function-like Macro</h2>
<p>It's finally time to have some <em>fun</em> again. We are going to start simple, but the second example is going to include parsing custom syntax. <em>Fun</em>, right?</p>
<p>Disclaimer: If you're familiar with declarative macros (using <code>macro_rules!</code> syntax), you might realize that the following examples can easily be written using that syntax and don't need to be procedural macros. Writing example procedural macros that cannot be written as declarative ones is extremely difficult if you also want to keep things simple, which is why the examples were chosen despite this.</p>
<h3 id="heading-the-constantstring-macro">The <code>constant_string</code> Macro</h3>
<p>We're going to build a very simple macro that takes in a string literal (of type <code>&amp;str</code>) as input and creates a global public constant for it (the name of the variable being the same as the value). Basically, our macro will generate the following:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">pub</span> <span class="hljs-keyword">const</span> STRING_LITERAL: &amp;<span class="hljs-built_in">str</span> = <span class="hljs-string">"STRING_LITERAL"</span>;
</code></pre>
<h3 id="heading-how-to-declare-a-function-like-macro">How to Declare a Function-like Macro</h3>
<p>You declare function-like macros by creating a function and annotating that function using a <code>proc_macro</code> macro. It tells the compiler to consider that function as a macro declaration. Let's see what that looks like:‌</p>
<pre><code class="lang-rust"><span class="hljs-comment">// my-app-macros/src/lib.rs</span>

<span class="hljs-meta">#[proc_macro]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">constant_string</span></span>(item: TokenStream) -&gt; TokenStream {
    constant_string_impl(item)
}
</code></pre>
<p>For these macros, the function name is important, as that also becomes the name of the macro. As you can see, these only take a single argument, which is whatever you pass on to the macro. It can literally be anything, even custom syntax that's not valid Rust code.</p>
<h3 id="heading-how-to-implement-the-constantstring-macro">How to Implement the <code>constant_string</code> Macro</h3>
<p>For the implementation, let's create a new file <code>constant_string.rs</code>:</p>
<pre><code class="lang-rust">touch my-app-macros/src/constant_string.rs
</code></pre>
<p>The implementation is pretty simple:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> darling::FromMeta;
<span class="hljs-keyword">use</span> proc_macro::TokenStream;
<span class="hljs-keyword">use</span> quote::quote;
<span class="hljs-keyword">use</span> syn::{parse_macro_input, Ident, LitStr};

<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">constant_string_impl</span></span>(item: TokenStream) -&gt; TokenStream {
    <span class="hljs-comment">// Parse input as a string literal</span>
    <span class="hljs-keyword">let</span> constant_value = parse_macro_input!(item <span class="hljs-keyword">as</span> LitStr);

    <span class="hljs-comment">// Create a new `Ident` (identifier) from the passed string value.</span>
    <span class="hljs-comment">// This is going to be the name of the constant variable.</span>
    <span class="hljs-keyword">let</span> constant_value_name = Ident::from_string(&amp;constant_value.value()).unwrap();

    <span class="hljs-comment">// Generate the code for declaring the constant variable.</span>
    quote!(<span class="hljs-keyword">pub</span> <span class="hljs-keyword">const</span> #constant_value_name: &amp;<span class="hljs-built_in">str</span> = #constant_value;).into()
}
</code></pre>
<p>All we're doing is parsing input as a string literal. If you pass something to do this is not a string literal, it will throw an error. Then we take the string, create a identifier out of it, and generate the output code. Short and simple.</p>
<h3 id="heading-how-to-use-the-constantstring-macro">How to Use the <code>constant_string</code> Macro</h3>
<p>The usage of this macro is also pretty simple:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/main.rs</span>

constant_string!(<span class="hljs-string">"SOME_CONSTANT_STRING_VALUE"</span>);
</code></pre>
<p>The above code will expand to this:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">pub</span> <span class="hljs-keyword">const</span> SOME_CONSTANT_STRING_VALUE: &amp;<span class="hljs-built_in">str</span> = <span class="hljs-string">"SOME_CONSTANT_STRING_VALUE"</span>;
</code></pre>
<h2 id="heading-a-more-elaborate-function-like-macro">A More Elaborate Function-like Macro</h2>
<p>Function-like macros, as the name might suggest, can be used in a similar way to calling a function. You can also use them in any place where you can call a function, and beyond.</p>
<h3 id="heading-the-hashmapify-macro">The <code>hash_mapify</code> Macro</h3>
<p>Moving on to the interesting parts: the macro you're going to write now will allow you to generate a <code>HashMap</code> by simply passing in a list of key-value pairs. For example:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">let</span> variable = <span class="hljs-string">"Some variable"</span>;

hash_mapify!(
    &amp;<span class="hljs-built_in">str</span>,
    key = <span class="hljs-string">"value"</span>, 
    key2 = <span class="hljs-string">"value2"</span>, 
    key3 = <span class="hljs-string">"value3"</span>, 
    key4 = variable
);
</code></pre>
<p>As you can see, we want the first argument to be the type of the value, and the subsequent arguments to be the key-value pairs. And we'll need to parse all of this ourselves.</p>
<p>To keep things simple, since this can easily get out of hand, we're only going to support primitive values such as strings, integers, floats and booleans. So we're not going to support creating a <code>hash_map</code> with non-string keys or <code>enum</code> and <code>struct</code> as values.</p>
<h3 id="heading-how-to-implement-the-hashmapify-macro">How to Implement the <code>hash_mapify</code> Macro</h3>
<p>We're going to start as usual by declaring our macro:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// my-app-macros/src/lib.rs</span>

<span class="hljs-meta">#[proc_macro]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">hash_mapify</span></span>(item: TokenStream) -&gt; TokenStream {
    hash_mapify_impl(item)
}
</code></pre>
<p>Next, you're going to define a data structure to hold your input data. In this case, you need to know the value type passed, as well as a list of key-value pairs. </p>
<p>We are going to extract the implementation to a separate file, which is where you'll also implement the data types and parsing logic. </p>
<p>Create new file <code>hash_mapify.rs</code> and declare the data type to hold input data:</p>
<pre><code class="lang-shell">touch my-app-macros/src/hash_mapify.rs
</code></pre>
<h3 id="heading-how-to-parse-hashmapifys-input">How to Parse <code>hash_mapify</code>'s Input</h3>
<pre><code class="lang-rust"><span class="hljs-comment">// my-app-macros/src/hash_mapify.rs</span>

<span class="hljs-keyword">use</span> proc_macro::TokenStream;
<span class="hljs-keyword">use</span> quote::{quote, ToTokens};
<span class="hljs-keyword">use</span> syn::parse::{Parse, ParseStream};
<span class="hljs-keyword">use</span> syn::{parse_macro_input, Lit, LitStr, Token, Type};

<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ParsedMapEntry</span></span>(<span class="hljs-built_in">String</span>, proc_macro2::TokenStream);

<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ParsedMap</span></span> {
    value_type: Type,
    entries: <span class="hljs-built_in">Vec</span>&lt;ParsedMapEntry&gt;,
}
</code></pre>
<p>You store the value as <code>TokenStream</code> directly because you need to support both literal values as well as variables, both of which only have 1 common type in this context, <code>TokenStream</code>.</p>
<p>You also might have noticed that we save the <code>value_type</code> as <code>Type</code> which is a type provided by <code>syn</code> crate which is an enum of the possible types that a Rust value could have. That was a mouthful! </p>
<p>You won't need to handle each variant of this enum, since this type can also directly be converted to <code>TokenStream</code>. You'll better understand what that means shortly.</p>
<p>Next, you need to implement the <code>syn::parse::Parse</code> trait for <code>ParsedMap</code> declared previously, so that it can be computed from the <code>TokenStream</code> passed as arguments to the macro.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// my-app-macros/src/hash_mapify.rs</span>

<span class="hljs-keyword">impl</span> Parse <span class="hljs-keyword">for</span> ParsedMap {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">parse</span></span>(input: ParseStream) -&gt; syn::<span class="hljs-built_in">Result</span>&lt;<span class="hljs-keyword">Self</span>&gt; {
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> entries = <span class="hljs-built_in">Vec</span>::&lt;ParsedMapEntry&gt;::new();
    }
}
</code></pre>
<p><code>input</code>, which is of type <code>ParsedStream</code> in this case, works similar to an iterator. You need to parse tokens out of the input using the method <code>parse</code> on it, which will also advance the stream to the beginning of the next token. </p>
<p>For example, if you have a stream of tokens representing <code>[a, b, c]</code>, as soon as you parse <code>[</code> out of this stream, the stream will be mutated to only contain <code>a, b, c]</code> . This is very similar to iterators, where as soon as you take a value out, the iterator is advanced by one position and only holds the remaining items.</p>
<p>Before you parse anything, you need to check if input is empty, and panic if it is:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// my-app-macros/src/hash_mapify.rs</span>

<span class="hljs-keyword">impl</span> Parse <span class="hljs-keyword">for</span> ParsedMap {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">parse</span></span>(input: ParseStream) -&gt; syn::<span class="hljs-built_in">Result</span>&lt;<span class="hljs-keyword">Self</span>&gt; {
        <span class="hljs-comment">// ...</span>

        <span class="hljs-comment">// Check if input is empty (no arguments are passed). If</span>
        <span class="hljs-comment">// not, then panic as we cannot continue further.</span>
        <span class="hljs-keyword">if</span> input.is_empty() {
            <span class="hljs-built_in">panic!</span>(<span class="hljs-string">"At least a type must be specified for an empty hashmap"</span>);
        }

        <span class="hljs-comment">// ...</span>
    }
}
</code></pre>
<p>Since we expect the first argument passed to the macro to be the type of the value in our hashmap, let's parse that out of the token stream:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// my-app-macros/src/hash_mapify.rs</span>

<span class="hljs-keyword">impl</span> Parse <span class="hljs-keyword">for</span> ParsedMap {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">parse</span></span>(input: ParseStream) -&gt; syn::<span class="hljs-built_in">Result</span>&lt;<span class="hljs-keyword">Self</span>&gt; {
        <span class="hljs-comment">// ...</span>

        <span class="hljs-comment">// Since the first argument should be of type `Type`, you try</span>
        <span class="hljs-comment">// to parse `Type` out of input and returns an error otherwise.</span>
        <span class="hljs-keyword">let</span> ty = input.parse::&lt;Type&gt;()?;

        <span class="hljs-comment">// ...</span>
    }
}
</code></pre>
<p>Parse takes a single type argument which represents what to parse.</p>
<p>If the first argument cannot be parsed as a valid type, an error will be returned. Do note that this doesn't verify if the type you passed actually exists or not, this will only validate whether the tokens in the first argument are valid for a type definition, and that's all.</p>
<p>This means that if you pass <code>SomeRandomType</code> where <code>SomeRandomType</code> isn't actually defined, the parsing will still succeed. It will only fail after expanding the macro during compile time.</p>
<p>Moving on, we also expect the user to use <code>,</code> to separate the arguments. Let's parse that as the next token after type:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// my-app-macros/src/hash_mapify.rs</span>

<span class="hljs-keyword">impl</span> Parse <span class="hljs-keyword">for</span> ParsedMap {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">parse</span></span>(input: ParseStream) -&gt; syn::<span class="hljs-built_in">Result</span>&lt;<span class="hljs-keyword">Self</span>&gt; {
        <span class="hljs-comment">// ...</span>

        <span class="hljs-comment">// Next, parse the `,` token, which you expect to be used to</span>
        <span class="hljs-comment">// separate the arguments.</span>
        input.parse::&lt;Token![,]&gt;()?;

        <span class="hljs-comment">// ...</span>
    }
}
</code></pre>
<p>You might notice the usage of the <code>Token!</code> macro when providing the type argument for the <code>parse</code> method. It's a macro provided by <code>syn</code> to easily convert built-ins such as keywords (<code>type</code>, <code>async</code> , <code>fn</code> and so on) as well as punctuation marks (<code>,</code>, <code>.</code>, <code>;</code> and so on) and delimiters (<code>{</code>, <code>[</code>, <code>(</code> and so on). This macro takes a single argument, which is the keyword/punctuation/delimiter literal for which the type is needed. </p>
<p>The official docs define it as:</p>
<blockquote>
<p>A type-macro that expands to the name of the Rust type representation of a given token.</p>
</blockquote>
<p>Now that you have the type of value as well as the first separator (comma), it's time to start parsing key-value pairs. All of the key-value pairs follow the same structure <code>key = value</code> and are separated by commas.</p>
<p>Do note that white-space isn't important, as that is entirely handled during the tokenization process and isn't something that you need to handle.</p>
<p>Since you won't know how many key-value pairs are passed, you need something to tell you when all of it is parsed:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// my-app-macros/src/hash_mapify.rs</span>

<span class="hljs-keyword">impl</span> Parse <span class="hljs-keyword">for</span> ParsedMap {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">parse</span></span>(input: ParseStream) -&gt; syn::<span class="hljs-built_in">Result</span>&lt;<span class="hljs-keyword">Self</span>&gt; {
        <span class="hljs-comment">// ...</span>

        <span class="hljs-comment">// Loop until the input is empty (there is nothing else</span>
        <span class="hljs-comment">// left to parse).</span>
        <span class="hljs-keyword">while</span> !input.is_empty() {
            <span class="hljs-comment">// ..</span>
        }

        <span class="hljs-comment">// ...</span>
    }
}
</code></pre>
<p>As I explained previously, tokens are taken out of the stream and it's advanced each time you parse something. This means that when all of the tokens are parsed, the stream will be empty. We utilise this fact here to figure out when to break out of the loop.</p>
<p>Each key-value pair can be parsed in a similar fashion as you parsed the type argument:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// my-app-macros/src/hash_mapify.rs</span>

<span class="hljs-keyword">impl</span> Parse <span class="hljs-keyword">for</span> ParsedMap {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">parse</span></span>(input: ParseStream) -&gt; syn::<span class="hljs-built_in">Result</span>&lt;<span class="hljs-keyword">Self</span>&gt; {
        <span class="hljs-comment">// ...</span>

        <span class="hljs-comment">// Loop until the input is empty (there is nothing else</span>
        <span class="hljs-comment">// left to parse).</span>
        <span class="hljs-keyword">while</span> !input.is_empty() {
            <span class="hljs-comment">// Try to parse the key as an identifier</span>
            <span class="hljs-keyword">let</span> key = <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Ok</span>(key) = input.parse::&lt;syn::Ident&gt;() {
                key.to_string()
                <span class="hljs-comment">// If it's not an identifier, try to parse it as</span>
                <span class="hljs-comment">// a string literal</span>
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Ok</span>(key) = input.parse::&lt;LitStr&gt;() {
                key.value()
                <span class="hljs-comment">// If it's neither an identifier nor a string literal,</span>
                <span class="hljs-comment">// it is not a valid key, so panic with appropriate</span>
                <span class="hljs-comment">// error.</span>
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-built_in">panic!</span>(<span class="hljs-string">"Key must be either a string literal or an identifier!"</span>);
            };

            <span class="hljs-comment">// Parse the `=` sign, which should be the next token after</span>
            <span class="hljs-comment">// a key.</span>
            input.parse::&lt;Token![=]&gt;()?;

            <span class="hljs-comment">// Next, try to parse the value as an identifier. If it is, it</span>
            <span class="hljs-comment">// means that it's a variable, so we should convert it to token</span>
            <span class="hljs-comment">// stream directly.</span>
            <span class="hljs-keyword">let</span> value = <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Ok</span>(value) = input.parse::&lt;syn::Ident&gt;() {
                value.to_token_stream()
                <span class="hljs-comment">// If the input isn't an identifier, try to parse it as a</span>
                <span class="hljs-comment">// literal value such as `"string"` for strings, `42`</span>
                <span class="hljs-comment">// for numbers `false` for boolean value, etc.</span>
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Ok</span>(value) = input.parse::&lt;Lit&gt;() {
                value.to_token_stream()
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-comment">// If the input is neither an identifier nor a literal value</span>
                <span class="hljs-comment">// panic with appropriate error.</span>
                <span class="hljs-built_in">panic!</span>(<span class="hljs-string">"Value must be either a literal or an identifier!"</span>);
            };

            <span class="hljs-comment">// Push the parsed key value pair to our list.</span>
            entries.push(ParsedMapEntry(key, value));

            <span class="hljs-comment">// Check if next token is a comma, without advancing the stream</span>
            <span class="hljs-keyword">if</span> input.peek(Token![,]) {
                <span class="hljs-comment">// If it is, then parse it out and advance the stream before</span>
                <span class="hljs-comment">// moving on to the next key-value pair</span>
                input.parse::&lt;Token![,]&gt;()?;
            }
        }

        <span class="hljs-comment">// ...</span>
    }
}
</code></pre>
<p>The only thing here that is new, is the call to <code>peek</code> method at the end. This is a special method that returns a boolean if the token that is passed to <code>peek</code> is the next token in the stream, and false otherwise.</p>
<p>As the name might suggest, this only performs a check, so it doesn't take that token out of the stream or advance the stream in any form.</p>
<p>Once all of the parsing is done, you just return the information as part of <code>ParsedMap</code> struct we declared earlier. The complete implementation for this trait is as below if that's easier for you to read through:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// my-app-macros/src/hash_mapify.rs</span>

<span class="hljs-keyword">impl</span> Parse <span class="hljs-keyword">for</span> ParsedMap {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">parse</span></span>(input: ParseStream) -&gt; syn::<span class="hljs-built_in">Result</span>&lt;<span class="hljs-keyword">Self</span>&gt; {
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> entries = <span class="hljs-built_in">Vec</span>::&lt;ParsedMapEntry&gt;::new();

        <span class="hljs-comment">// Check if input is empty (no arguments are passed). If not, then</span>
        <span class="hljs-comment">// panic as we cannot continue further.</span>
        <span class="hljs-keyword">if</span> input.is_empty() {
            <span class="hljs-built_in">panic!</span>(<span class="hljs-string">"At least a type must be specified for an empty hashmap"</span>);
        }

        <span class="hljs-comment">// Since the first argument should be of type `Type`, you try</span>
        <span class="hljs-comment">// to parse `Type` out of input and returns an error otherwise.</span>
        <span class="hljs-keyword">let</span> ty = input.parse::&lt;Type&gt;()?;

        <span class="hljs-comment">// Next, parse the `,` token, which you expect to be used to</span>
        <span class="hljs-comment">// separate the arguments.</span>
        input.parse::&lt;Token![,]&gt;()?;

        <span class="hljs-comment">// Loop until the input is empty (there is nothing else</span>
        <span class="hljs-comment">// left to parse).</span>
        <span class="hljs-keyword">while</span> !input.is_empty() {
            <span class="hljs-comment">// Try to parse the key as an identifier</span>
            <span class="hljs-keyword">let</span> key = <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Ok</span>(key) = input.parse::&lt;syn::Ident&gt;() {
                key.to_string()
                <span class="hljs-comment">// If it's not an identifier, try to parse it as</span>
                <span class="hljs-comment">// a string literal</span>
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Ok</span>(key) = input.parse::&lt;LitStr&gt;() {
                key.value()
                <span class="hljs-comment">// If it's neither an identifier nor a string literal,</span>
                <span class="hljs-comment">// it is not a valid key, so panic with appropriate</span>
                <span class="hljs-comment">// error.</span>
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-built_in">panic!</span>(<span class="hljs-string">"Key must be either a string literal or an identifier!"</span>);
            };

            <span class="hljs-comment">// Parse the `=` sign, which should be the next token after</span>
            <span class="hljs-comment">// a key.</span>
            input.parse::&lt;Token![=]&gt;()?;

            <span class="hljs-comment">// Next, try to parse the value as an identifier. If it is, it</span>
            <span class="hljs-comment">// means that it's a variable, so we should convert it to token</span>
            <span class="hljs-comment">// stream directly.</span>
            <span class="hljs-keyword">let</span> value = <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Ok</span>(value) = input.parse::&lt;syn::Ident&gt;() {
                value.to_token_stream()
                <span class="hljs-comment">// If the input isn't an identifier, try to parse it as a</span>
                <span class="hljs-comment">// literal value such as `"string"` for strings, `42`</span>
                <span class="hljs-comment">// for numbers `false` for boolean value, etc.</span>
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Ok</span>(value) = input.parse::&lt;Lit&gt;() {
                value.to_token_stream()
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-comment">// If the input is neither an identifier nor a literal value</span>
                <span class="hljs-comment">// panic with appropriate error.</span>
                <span class="hljs-built_in">panic!</span>(<span class="hljs-string">"Value must be either a literal or an identifier!"</span>);
            };

            <span class="hljs-comment">// Push the parsed key value pair to our list.</span>
            entries.push(ParsedMapEntry(key, value));

            <span class="hljs-comment">// Check if next token is a comma, without advancing the stream</span>
            <span class="hljs-keyword">if</span> input.peek(Token![,]) {
                <span class="hljs-comment">// If it is, then parse it out and advance the stream before</span>
                <span class="hljs-comment">// moving on to the next key-value pair</span>
                input.parse::&lt;Token![,]&gt;()?;
            }
        }

        <span class="hljs-literal">Ok</span>(ParsedMap {
            value_type: ty,
            entries,
        })
    }
}
</code></pre>
<h3 id="heading-how-to-generate-the-output-code">How to Generate the Output Code</h3>
<p>You can now finally write the actual macro implementation, which is going to be pretty-straightforward:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// my-app-macros/src/hash_mapify.rs</span>

<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">hash_mapify_impl</span></span>(item: TokenStream) -&gt; TokenStream {
    <span class="hljs-comment">// Parse input token stream as `ParsedMap` defined by us.</span>
    <span class="hljs-comment">// This will use the logic from parse trait we implemented</span>
    <span class="hljs-comment">// earlier.</span>
    <span class="hljs-keyword">let</span> input = parse_macro_input!(item <span class="hljs-keyword">as</span> ParsedMap);

    <span class="hljs-keyword">let</span> key_value_pairs = input.entries;
    <span class="hljs-keyword">let</span> ty = input.value_type;

    <span class="hljs-comment">// Generate the output hashmap inside a code block so that</span>
    <span class="hljs-comment">// we don't shadow any existing variables. Return the hashmap</span>
    <span class="hljs-comment">// from the block.</span>
    quote!({
        <span class="hljs-comment">// Create a new hashmap with `String` for key type and `#ty` for </span>
        <span class="hljs-comment">// value type, which parsed from the macro input arguments.</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> hash_map = std::collections::HashMap::&lt;<span class="hljs-built_in">String</span>, #ty&gt;::new();

        <span class="hljs-comment">// Insert all key-value pairs into the hashmap.</span>
        #(
            hash_map.insert(#key_value_pairs);
        )*

        <span class="hljs-comment">// Return the generated hashmap</span>
        hash_map
    })
    .into()
}
</code></pre>
<p>If you're coding along with me, or if you have a keen eye, you might have noticed that there is an error here. The type of variable <code>key_value_pairs</code> is <code>Vec&lt;ParsedMapEntry&gt;</code>. We are trying to use it in the output as:</p>
<pre><code class="lang-rust">#(hash_map.insert(#key_value_pairs);)*
</code></pre>
<p>which is the correct syntax for working with lists, but the underlying type <code>ParsedMapEntry</code> is a custom type. And neither <code>syn</code> nor <code>quote</code> would know how to convert it to a token stream. So we cannot use it with this syntax.</p>
<p>But if we try to manually write an implementation where we loop it ourselves, generate a separate tokens stream in each loop, and extend the existing one, it's going to be quite tedious. Wouldn't it be great if there was a better solution? Turns out there is: <code>ToTokens</code> trait.</p>
<h3 id="heading-how-to-convert-custom-data-types-to-output-tokens">How to Convert Custom Data Types to Output Tokens</h3>
<p>This trait can be implemented for any of our custom types and defines how the type looks like when converted into the token stream. </p>
<pre><code class="lang-rust"><span class="hljs-comment">// my-app-macros/src/hash_mapify.rs</span>

<span class="hljs-keyword">impl</span> ToTokens <span class="hljs-keyword">for</span> ParsedMapEntry {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">to_tokens</span></span>(&amp;<span class="hljs-keyword">self</span>, tokens: &amp;<span class="hljs-keyword">mut</span> proc_macro2::TokenStream) {
        <span class="hljs-keyword">let</span> key = <span class="hljs-keyword">self</span>.<span class="hljs-number">0</span>.clone();
        <span class="hljs-keyword">let</span> value = <span class="hljs-keyword">self</span>.<span class="hljs-number">1</span>.clone();

        tokens.extend(quote!(<span class="hljs-built_in">String</span>::from(#key), #value));
    }
}
</code></pre>
<p>As part of the implementation, you need to mutate the <code>tokens</code> argument and extend it to contain the token stream that we want our type to generate. The syntax I used to do that should all be familiar by now.</p>
<p>Once you've done this, <code>quote</code> can now easily convert the problematic code to token stream. So this: <code>#(hash_map.insert(#key_value_pairs);)*</code> will now work directly.</p>
<p>As usual, here's the complete implementation if that's easier to understand:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// my-app-macros/src/hash_mapify.rs</span>

<span class="hljs-keyword">use</span> proc_macro::TokenStream;
<span class="hljs-keyword">use</span> quote::{quote, ToTokens};
<span class="hljs-keyword">use</span> syn::parse::{Parse, ParseStream};
<span class="hljs-keyword">use</span> syn::{parse_macro_input, Lit, LitStr, Token, Type};

<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ParsedMapEntry</span></span>(<span class="hljs-built_in">String</span>, proc_macro2::TokenStream);

<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ParsedMap</span></span> {
    value_type: Type,
    entries: <span class="hljs-built_in">Vec</span>&lt;ParsedMapEntry&gt;,
}

<span class="hljs-keyword">impl</span> ToTokens <span class="hljs-keyword">for</span> ParsedMapEntry {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">to_tokens</span></span>(&amp;<span class="hljs-keyword">self</span>, tokens: &amp;<span class="hljs-keyword">mut</span> proc_macro2::TokenStream) {
        <span class="hljs-keyword">let</span> key = <span class="hljs-keyword">self</span>.<span class="hljs-number">0</span>.clone();
        <span class="hljs-keyword">let</span> value = <span class="hljs-keyword">self</span>.<span class="hljs-number">1</span>.clone();

        tokens.extend(quote!(<span class="hljs-built_in">String</span>::from(#key), #value));
    }
}

<span class="hljs-keyword">impl</span> Parse <span class="hljs-keyword">for</span> ParsedMap {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">parse</span></span>(input: ParseStream) -&gt; syn::<span class="hljs-built_in">Result</span>&lt;<span class="hljs-keyword">Self</span>&gt; {
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> entries = <span class="hljs-built_in">Vec</span>::&lt;ParsedMapEntry&gt;::new();

        <span class="hljs-comment">// Check if input is empty (no arguments are passed). If not, then</span>
        <span class="hljs-comment">// panic as we cannot continue further.</span>
        <span class="hljs-keyword">if</span> input.is_empty() {
            <span class="hljs-built_in">panic!</span>(<span class="hljs-string">"At least a type must be specified for an empty hashmap"</span>);
        }

        <span class="hljs-comment">// Since the first argument should be of type `Type`, you try</span>
        <span class="hljs-comment">// to parse `Type` out of input and returns an error otherwise.</span>
        <span class="hljs-keyword">let</span> ty = input.parse::&lt;Type&gt;()?;

        <span class="hljs-comment">// Next, parse the `,` token, which you expect to be used to</span>
        <span class="hljs-comment">// separate the arguments.</span>
        input.parse::&lt;Token![,]&gt;()?;

        <span class="hljs-comment">// Loop until the input is empty (there is nothing else</span>
        <span class="hljs-comment">// left to parse).</span>
        <span class="hljs-keyword">while</span> !input.is_empty() {
            <span class="hljs-comment">// Try to parse the key as an identifier</span>
            <span class="hljs-keyword">let</span> key = <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Ok</span>(key) = input.parse::&lt;syn::Ident&gt;() {
                key.to_string()
                <span class="hljs-comment">// If it's not an identifier, try to parse it as</span>
                <span class="hljs-comment">// a string literal</span>
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Ok</span>(key) = input.parse::&lt;LitStr&gt;() {
                key.value()
                <span class="hljs-comment">// If it's neither an identifier nor a string literal,</span>
                <span class="hljs-comment">// it is not a valid key, so panic with appropriate</span>
                <span class="hljs-comment">// error.</span>
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-built_in">panic!</span>(<span class="hljs-string">"Key must be either a string literal or an identifier!"</span>);
            };

            <span class="hljs-comment">// Parse the `=` sign, which should be the next token after</span>
            <span class="hljs-comment">// a key.</span>
            input.parse::&lt;Token![=]&gt;()?;

            <span class="hljs-comment">// Next, try to parse the value as an identifier. If it is, it</span>
            <span class="hljs-comment">// means that it's a variable, so we should convert it to token</span>
            <span class="hljs-comment">// stream directly.</span>
            <span class="hljs-keyword">let</span> value = <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Ok</span>(value) = input.parse::&lt;syn::Ident&gt;() {
                value.to_token_stream()
                <span class="hljs-comment">// If the input isn't an identifier, try to parse it as a</span>
                <span class="hljs-comment">// literal value such as `"string"` for strings, `42`</span>
                <span class="hljs-comment">// for numbers `false` for boolean value, etc.</span>
            } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> <span class="hljs-keyword">let</span> <span class="hljs-literal">Ok</span>(value) = input.parse::&lt;Lit&gt;() {
                value.to_token_stream()
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-comment">// If the input is neither an identifier nor a literal value</span>
                <span class="hljs-comment">// panic with appropriate error.</span>
                <span class="hljs-built_in">panic!</span>(<span class="hljs-string">"Value must be either a literal or an identifier!"</span>);
            };

            <span class="hljs-comment">// Push the parsed key value pair to our list.</span>
            entries.push(ParsedMapEntry(key, value));

            <span class="hljs-comment">// Check if next token is a comma, without advancing the stream</span>
            <span class="hljs-keyword">if</span> input.peek(Token![,]) {
                <span class="hljs-comment">// If it is, then parse it out and advance the stream before</span>
                <span class="hljs-comment">// moving on to the next key-value pair</span>
                input.parse::&lt;Token![,]&gt;()?;
            }
        }

        <span class="hljs-literal">Ok</span>(ParsedMap {
            value_type: ty,
            entries,
        })
    }
}

<span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">hash_mapify_impl</span></span>(item: TokenStream) -&gt; TokenStream {
    <span class="hljs-comment">// Parse input token stream as `ParsedMap` defined by us.</span>
    <span class="hljs-comment">// This will use the logic from parse trait we implemented</span>
    <span class="hljs-comment">// earlier.</span>
    <span class="hljs-keyword">let</span> input = parse_macro_input!(item <span class="hljs-keyword">as</span> ParsedMap);

    <span class="hljs-keyword">let</span> key_value_pairs = input.entries;
    <span class="hljs-keyword">let</span> ty = input.value_type;

    <span class="hljs-comment">// Generate the output hashmap inside a code block so that</span>
    <span class="hljs-comment">// we don't shadow any existing variables. Return the hashmap</span>
    <span class="hljs-comment">// from the block.</span>
    quote!({
        <span class="hljs-comment">// Create a new hashmap with `String` for key type and `#ty` for</span>
        <span class="hljs-comment">// value type, which parsed from the macro input arguments.</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> hash_map = std::collections::HashMap::&lt;<span class="hljs-built_in">String</span>, #ty&gt;::new();

        <span class="hljs-comment">// Insert all key-value pairs into the hashmap.</span>
        #(
            hash_map.insert(#key_value_pairs);
        )*

        <span class="hljs-comment">// Return the generated hashmap</span>
        hash_map
    })
    .into()
}
</code></pre>
<h3 id="heading-how-to-use-the-hashmapify-macro">How to Use the <code>hash_mapify</code> Macro</h3>
<p>We can verify that our macro works by writing a simple usage:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// src/main.rs</span>

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    test_hashmap();
}

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">test_hashmap</span></span>() {
    <span class="hljs-keyword">let</span> some_variable = <span class="hljs-string">"Some variable value"</span>;

    <span class="hljs-keyword">let</span> hash_map = hash_mapify!(
        &amp;<span class="hljs-built_in">str</span>,
        <span class="hljs-string">"first_key"</span> = <span class="hljs-string">"first_value"</span>,
        <span class="hljs-string">"second_variable"</span> = some_variable,
        some_key = <span class="hljs-string">"value for variable key"</span>,
    );

    <span class="hljs-keyword">let</span> number_hash_map =
        hash_mapify!(<span class="hljs-built_in">usize</span>, <span class="hljs-string">"first_key"</span> = <span class="hljs-number">1</span>, <span class="hljs-string">"second_variable"</span> = <span class="hljs-number">2</span>, some_key = <span class="hljs-number">3</span>,);

    dbg!(hash_map);
    dbg!(number_hash_map);
}
</code></pre>
<p>If you run this code, you should see the following output:</p>
<pre><code class="lang-shell">[src/main.rs:62:5] hash_map = {
    "first_key": "first_value",
    "some_key": "value for variable key",
    "second_variable": "Some variable value",
}
[src/main.rs:63:5] number_hash_map = {
    "second_variable": 2,
    "first_key": 1,
    "some_key": 3,
}
</code></pre>
<p>which is what we would expect to happen.</p>
<p>And now that we've covered all three types of procedural macros, we're going to wrap up the examples here.</p>
<h2 id="heading-beyond-writing-macros">Beyond Writing Macros</h2>
<p>Now that you've learned how to write basic derive macros, I'd like to take some time to quickly introduce some additional tools and techniques that will be helpful when working with macros. I'll also point out some drawbacks of why and when to avoid them.</p>
<h3 id="heading-helpful-cratestools">Helpful Crates/Tools</h3>
<p><a target="_blank" href="https://github.com/dtolnay/cargo-expand"><strong>cargo-expand</strong></a></p>
<p>This is a CLI tool that can generate macro expanded code for any file in your project. Another great project by <a target="_blank" href="https://crates.io/users/dtolnay">David Tolnay</a>. You do need the nightly toolchain for Rust to use this, though. Don't worry – it's only required for the tool itself to work. You don't need to make your project use the nightly toolchain as well. Your project can stay in the stable zone.</p>
<p>Install nightly toolchain:</p>
<pre><code class="lang-shell">rustup toolchain install nightly
</code></pre>
<p>Install <code>cargo-expand</code>:</p>
<pre><code class="lang-shell">cargo install cargo-expand
</code></pre>
<p>Now that this is done, you can see what the actual expansion of your code in main looks like. Simply run the following in the <code>my-app</code> project directory:</p>
<pre><code class="lang-shell">cargo expand
</code></pre>
<p>and it will output the expanded code in the terminal output. You will see some unfamiliar stuff as well, such as what the <code>dbg!</code> macro expands to, but you can ignore those.</p>
<p><strong><a target="_blank" href="https://docs.rs/trybuild/latest/trybuild/#">trybuild</a> &amp; <a target="_blank" href="https://docs.rs/macrotest/latest/macrotest/#">macrotest</a></strong></p>
<p>These are 2 crates that are extremely useful if you want to unit-test your procedural macros' expanded forms, or assert any expected compilation errors.</p>
<h2 id="heading-downsides-of-macros">Downsides of Macros</h2>
<h3 id="heading-debugging-or-lack-thereof">Debugging (or lack thereof)</h3>
<p>You cannot put a breakpoint into any line of code that is generated by the macro. Nor can you get to it from the stacktrace of an error. This makes debugging generated code very difficult. </p>
<p>In my usual workflow, I either put logging into the generated code, or if that is not enough, I replace the usage of macro with the code given to me by <code>cargo expand</code> temporarily to debug it, make changes, and then update the macro code based on that. </p>
<p>There might be better ways out there, and if you know any, I'd be grateful if you can share them with me.</p>
<h3 id="heading-compile-time-costs">Compile Time Costs</h3>
<p>There's a non-zero cost for macro expansion that the compiler needs to run and process, and then check that the code it generated is valid. This becomes even more expensive when recursive macros are involved. </p>
<p>As a very crude estimation, each macro expansion adds 10ms to the compile time of the project. If you're interested, I encourage you to read through this <a target="_blank" href="https://rustc-dev-guide.rust-lang.org/macro-expansion.html">introduction on how the compiler processes macros</a> internally.</p>
<h3 id="heading-lack-of-auto-complete-and-code-checks">Lack of Auto-complete and Code Checks</h3>
<p>Code written as part of a macro output isn't presently supported fully by any IDE, nor is it supported by rust-analyzer. So in most cases, you're writing code without relying on features such as auto-complete, auto-suggestions, and so on.</p>
<h3 id="heading-where-do-we-draw-the-line">Where Do We Draw the Line?</h3>
<p>Given the insane potential of macros, it's very easy to get carried away with them. It's important to remember all of the drawbacks and make decisions accordingly, ensuring that you are not indulging yourselves into premature abstraction. </p>
<p>As a general rule, I personally avoid implementing any "business logic" with macros, nor do I attempt to write macros for generating code that I will need to step through with a debugger time and again. Or the code that I will need to make micro changes in for performance testing and improvement.</p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>This was a long journey! But I wanted anyone with basic knowledge and experience with Rust to be able to follow and come out of this able to write macros in their own projects. </p>
<p>Hopefully, I was able to do that for you. I will be writing a lot more about macros in general, so stay tuned for that.</p>
<p>You can find the complete code for everything we looked at in this article in <a target="_blank" href="https://github.com/anshulsanghi-blog/macros-handbook">https://github.com/anshulsanghi-blog/macros-handbook</a> repository.</p>
<p>Also, feel free to <strong><a target="_blank" href="mailto:contact@anshulsanghi.tech">contact me</a></strong> if you have any questions or opinions on this topic.</p>
<h3 id="heading-enjoying-my-work"><strong>Enjoying my work?</strong></h3>
<p>Consider buying me a coffee to support my work!</p>
<p><a target="_blank" href="https://buymeacoffee.com/anshulsanghi">☕Buy me a coffee</a></p>
<p>Till next time, happy coding and wishing you clear skies!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Rust Tutorial – How to Build a Naïve Star Detector for Images ]]>
                </title>
                <description>
                    <![CDATA[ Star detection is a crucial step in many of the processing and analysis routines that we perform on astronomical images. It is extremely important for a process called plate-solving, which is the process of figuring out which part of the sky an image... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/rust-tutorial-naive-star-detector-for-images/</link>
                <guid isPermaLink="false">66bb57a029aa951a4c0628bc</guid>
                
                    <category>
                        <![CDATA[ image processing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Rust ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Anshul Sanghi ]]>
                </dc:creator>
                <pubDate>Tue, 16 Apr 2024 19:34:07 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/04/cover.jpg" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Star detection is a crucial step in many of the processing and analysis routines that we perform on astronomical images. It is extremely important for a process called plate-solving, which is the process of figuring out which part of the sky an image shows, or which part of the sky your telescope is pointed at. </p>
<p>All modern telescope mounts can make use of plate solving software to automatically figure out where they're pointed at, and in which direction they need to move to point at the correct location.</p>
<p>Star detection, sometimes, is also used in correcting the effect of atmosphere on the sharpness of targets such as galaxies. It is also crucial for combining astronomical images from multiple nights, telescopes, locations and so on into a single output image that has a very high signal-to-noise ratio.</p>
<p>With this tutorial, I'd like to introduce a very naïve technique for detecting stars in an image.</p>
<h3 id="heading-a-quick-note">A quick note:</h3>
<p>Star detection is a very complex topic, and I've only scratched the surface both in my own understanding and in this article. </p>
<p>The steps I use and describe in this article are derived from public documentation on existing real world applications (both for star detection and for edge detection), as well as some blog posts from incredibly knowledgeable people (which I link to at the end of the article, be sure to check them out).</p>
<p>As such, this implementation is intended for learning purposes only.</p>
<h2 id="heading-before-you-read"><strong>Before You Read</strong></h2>
<h3 id="heading-prerequisites-for-the-first-part-of-the-tutorial"><strong>Prerequisites for the first part of the tutorial</strong></h3>
<p>The process described builds upon the concept of <a target="_blank" href="https://www.freecodecamp.org/news/multi-scale-analysis-of-images-in-rust/">multi-scale processing of images using a trous wavelet transform</a>. If you're not aware of what that is, I encourage you to learn more about it using my previous article that I just linked to, and then come back to this one.</p>
<p>This article also assumes that you have a basic understanding of <a target="_blank" href="https://en.wikipedia.org/wiki/Centroid">Centroids</a>. Just knowing what they mean is enough, as you don't have to calculate them yourself. Since the article focuses on image processing and analysis, a basic understanding of how pixels work in digital format is helpful, but not mandatory.</p>
<h3 id="heading-prerequisites-for-the-second-part-of-this-tutorial"><strong>Prerequisites for the second part of this tutorial</strong></h3>
<p>Here, we focus on implementing the algorithm using the Rust programming language, without going much into the details of the language itself. So being comfortable writing Rust programs, and comfortable reading crate documentations is required.</p>
<p>If this is not you, you can still read Part 1 and learn the technique, and then maybe you'll want to then try it out in a language of your choice. </p>
<p>If you're not familiar with Rust, I highly encourage you to learn the basics. <a target="_blank" href="https://www.freecodecamp.org/news/rust-in-replit/">Here's an interactive Rust course</a> that can get you started.</p>
<h2 id="heading-table-of-contents">Table of contents</h2>
<ol>
<li><a class="post-section-overview" href="#heading-how-star-detection-works-1">How Star Detection Works</a><ol>
<li><a class="post-section-overview" href="#heading-what-is-star-detection">What is Star Detection?</a></li>
<li><a class="post-section-overview" href="#heading-how-star-detection-works-1">How Star Detection Works</a></li>
<li><a class="post-section-overview" href="#heading-an-intermediary-look-at-the-process">An Intermediary Look at the Process</a></li>
<li><a class="post-section-overview" href="#heading-picking-it-apart">Picking It Apart</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#heading-how-to-implement-it-in-rust">How to Implement it in Rust</a><ol>
<li><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></li>
<li><a class="post-section-overview" href="#heading-how-to-read-and-decompose-the-input-image">How to read and decompose the input image</a></li>
<li><a class="post-section-overview" href="#heading-noise-reduction">Noise reduction</a></li>
<li><a class="post-section-overview" href="#heading-how-to-optimize-the-threshold-and-binarization">How to optimize the threshold and binarization</a></li>
<li><a class="post-section-overview" href="#heading-how-to-construct-polygons-around-stars">How to construct polygons around stars</a></li>
<li><a class="post-section-overview" href="#heading-how-to-detect-star-size-and-location-using-contours">How to detect star size and location using contours</a></li>
<li><a class="post-section-overview" href="#heading-how-to-encapsulate-the-process">How to encapsulate the process</a></li>
<li><a class="post-section-overview" href="#heading-how-to-test-the-implementation-on-astronomical-images">How to test the implementation on astronomical images</a></li>
<li><a class="post-section-overview" href="#heading-how-to-optimize-minimum-star-count">How to optimize minimum star count</a></li>
<li><a class="post-section-overview" href="#heading-but-there-is-one-more-thing">But there is one more thing...</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#heading-further-reading">Further Reading</a></li>
<li><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></li>
</ol>
<h2 id="heading-how-star-detection-works">How Star Detection Works</h2>
<p>Since this process involves a lot of steps, let's see how it works, with an increasing level of detail about what actually happens as we go along. With each increasing level, we'll be unwrapping the black box bit by bit.</p>
<h3 id="heading-what-is-star-detection">What is Star Detection?</h3>
<p>Star detection, at it's simplest form, involves isolating the stars from the rest of the image, and then performing edge detection on it.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/m42-star-detection-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>1. Input image</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/level-1-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>2. Detected stars visualised using green circles</em></p>
<h3 id="heading-how-star-detection-works-1">How Star Detection Works</h3>
<p>First, you try to extract away the pixels that you think might be stars from the rest of the pixels in the image. This new image, that only contains the extracted pixels, is then analysed using edge detection techniques to find the star positions in 2D space.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/m42-star-detection-2.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>1. Input image</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/level-1-2-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>2. Extracted pixels that are potentially stars</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/level-1-1-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>3. Detected stars visualised using green circles</em></p>
<h3 id="heading-an-intermediary-look-at-the-process">An Intermediary Look At The Process</h3>
<p>Then, you decompose your input image into multiple layers, each layer containing a part of the original data such that adding all layers gives us back the original data. </p>
<p>You then isolate the layers that would only contain small sized structures, such as noise and stars, and throw away the rest of the data.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/layers-of-structure.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Different layers of structure in the image that the input is decomposed into. We throw away the final layer and retain the rest in this example</em></p>
<p>With this filtered data, you find the edges in the image using the contouring technique (which is explained in the next section). Each contour gives us multiple "points" in the 2D space. You then try to draw a closed shape using the points you have. </p>
<p>Once you've done this, all you need is to find the center of this shape and you have the location of the stars.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/m42-star-detection-5.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>1. Input image</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/level-3-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>2. Image after decomposing into layers and throwing away large scale data</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/level-1-2-2.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>3. Image after binarisation</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/polygon.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>4. Detected contours visualised using green outlines</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/level-1-1-2.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>5. Detected stars visualised using green circles</em></p>
<h3 id="heading-picking-it-apart">Picking It Apart</h3>
<p>Using a multi-scale analysis technique facilitated by the à trous transform algorithm, you break down the image into multiple layers, each containing different scaled structures from the original image. You take the layers containing smaller scale structures and throw away the rest. </p>
<p>To these layers, you apply a bilateral denoising filter to reduce noise so that you can ensure that you're only left with stars and not noise that the algorithm might pick up as stars later on.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/decomposed-image.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Different layers of structure in the image that the input is decomposed into. We throw away the final layer and retain the rest in this example.</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/m42-star-detection-5-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>1. Input image</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/level-3-1-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>2. Image after decomposing into layers and throwing away large scale data</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/level-4-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>3. Noise reduced image</em></p>
<p>Once you've filtered out the noise, you binarize your image using thresholding. Thresholding and binarization is the process of converting all of the pixels to either pure black or pure white, so that they're easier to work with. You can do this by selecting a certain intensity value, and all pixels with intensity less than this become black and all pixels with intensity more than this become white. </p>
<p>To find the optimum intensity value to binarize the image with, you define a minimum number of stars that you expect to find in the image, which is usually determined based on what you actually need to do with your star locations. </p>
<p>In our example, we'll start with a minimum of 500 and slowly push it to the limit of the sample image to see what happens.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/level-1-2-3.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Binarizing noise-reduced and wavelet filtered image</em></p>
<p>This makes the process of edge detection (which is the next step in our process) using contouring much more reliable. </p>
<p>Contouring is a term that describes the process of figuring out where the structures are in your image, and drawing a border along those structures – these are known as contours. </p>
<p>It is similar to edge-detection, but edge-detection helps you differentiate between individual neighbouring pixels, whereas contours are designed to work with a complete boundary of any structures in an image.</p>
<p>The library we'll be using finds the contours in an image using the algorithm proposed by Suzuki and Abe: <a target="_blank" href="https://www.sciencedirect.com/science/article/abs/pii/0734189X85900167">Topological Structural Analysis of Digitized Binary Images by Border Following</a>. Contouring in this manner will give you a collection of points that lie on the border of each contour.</p>
<p>For each contour it finds, you create a polygon by joining all of the border points within that contour. If this shape is an open shape, then you just extrapolate the final border to create a polygon, which needs to be a closed shape. You then use the centroid formulae on this polygon to find the center of mass of your shape, which gives you the center of your star (in most cases). </p>
<p>You also need to find the euclidean distances between the center of mass and each border point, the longest of which becomes the size of the star.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/polygon-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Contouring the binarized image to find closed polygons around stars visualised here using green outlines</em></p>
<p>Once you have your star size, you reject any stars that are either smaller than 1 pixel or larger than 24 pixels. These are educated guesses that I use, and they seem to give me the best results for sample images (but this is definitely a potential point of improvement). </p>
<p>After all of this, you should have the x and y coordinates of the star, as well as its size in pixels.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/level-1-1-3.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Detected stars visualised using green circles around them</em></p>
<p>We're going to stop there, but there's a lot more that you can do after this step to remove false-positives and fix the centroid/size of stars. </p>
<h2 id="heading-how-to-implement-it-in-rust">How to Implement it in Rust</h2>
<p>Let's create a new library project:</p>
<pre><code class="lang-shell">cargo new --lib stardetect-rs &amp;&amp; cd stardetect-rs
</code></pre>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>You need a couple of dependencies to get started. Let's add them and I'll explain why you need them:</p>
<pre><code class="lang-shell">cargo add image imageproc image-dwt geo
</code></pre>
<ul>
<li><code>image</code> is a Rust library we'll use to work with images of all of the standard formats and encodings. It also helps us convert between various formats, and provides easy access to pixel data as buffers.</li>
<li><code>imageproc</code> is another library by the people who created the <code>image</code> library. It's an extension for the same as it implements image processing functions and algorithms for the <code>image</code> lib.</li>
<li><code>image-dwt</code> is my own library (shameless plug) that implements the <a target="_blank" href="https://www.freecodecamp.org/news/multi-scale-analysis-of-images-in-rust/">à trous wavelet decomposition algorithm</a> for <code>image</code> crate. This is needed to break down our image into multiple scales that I mentioned previously.</li>
<li><code>geo</code> is a Rust library that allows us to easily work with geometric types (like points in 2d space), shapes (such as polygons), and algorithms implemented for them. We use this library to build our polygon based on contour data, and to also find the centroid of the polygon that I described above. It also helps us compute euclidean distances between points, which we use for determining star size.</li>
</ul>
<h3 id="heading-how-to-read-and-decompose-the-input-image">How to read and decompose the input image</h3>
<p>You start by reading the input image and decomposing it so that you're only left with stars (and noise).</p>
<p>You need to define a new struct that will act as a wrapper for your input image, and add a constructor for it to create an instance of this struct based on input:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// lib.rs</span>
<span class="hljs-keyword">use</span> image::{DynamicImage, GrayImage};

<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">StarDetect</span></span> {
    source: GrayImage,
}

<span class="hljs-keyword">impl</span> <span class="hljs-built_in">From</span>&lt;DynamicImage&gt; <span class="hljs-keyword">for</span> StarDetect {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">from</span></span>(source: DynamicImage) -&gt; <span class="hljs-keyword">Self</span> {
        <span class="hljs-keyword">Self</span> {
            source: source.to_luma8(),
        }
    }
}
</code></pre>
<p>You then need to add the ability to extract the first <code>n</code> layers from wavelet decomposition of your image:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// lib.rs</span>

<span class="hljs-keyword">use</span> image_dwt::kernels::LinearInterpolationKernel;
<span class="hljs-keyword">use</span> image_dwt::recompose::{OutputLayer, RecomposableWaveletLayers};
<span class="hljs-keyword">use</span> image_dwt::transform::ATrousTransform;

<span class="hljs-keyword">impl</span> StarDetect {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">extract_small_scale_structures</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) {
        <span class="hljs-keyword">let</span> (width, height) = <span class="hljs-keyword">self</span>.source.dimensions();

        <span class="hljs-comment">// Decompose the image into 8 layers</span>
        <span class="hljs-keyword">let</span> filtered_image = ATrousTransform::new(
            &amp;DynamicImage::ImageLuma8(<span class="hljs-keyword">self</span>.source.clone()),
            <span class="hljs-number">8</span>,
            LinearInterpolationKernel,
        )
        <span class="hljs-comment">// Filter out the residue image and keep the rest</span>
        .filter(|item| item.pixel_scale.is_some())
        <span class="hljs-comment">// Recompose the first 3 layers into a grayscale image.</span>
        .recompose_into_image(width <span class="hljs-keyword">as</span> <span class="hljs-built_in">usize</span>, height <span class="hljs-keyword">as</span> <span class="hljs-built_in">usize</span>, OutputLayer::Grayscale);

        <span class="hljs-comment">// Update the source image that we will work with</span>
        <span class="hljs-comment">// going forward.</span>
        <span class="hljs-keyword">self</span>.source = filtered_image.to_luma8();
    }
}
</code></pre>
<h3 id="heading-noise-reduction">Noise reduction</h3>
<p>Now that you have the input image (which should only contain noise and stars), let's get rid of the noise:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// lib.rs</span>

<span class="hljs-keyword">impl</span> StarDetect {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">apply_noise_reduction</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) {
        <span class="hljs-keyword">self</span>.source = imageproc::filter::bilateral_filter(&amp;<span class="hljs-keyword">self</span>.source, <span class="hljs-number">10</span>, <span class="hljs-number">10</span>., <span class="hljs-number">3</span>.);
    }
}
</code></pre>
<p>Next, you need to determine the optimum threshold value for a given minimum star count. You find it by picking a value and iteratively optimising it until you hit a star count that's more than the minimum.</p>
<h3 id="heading-how-to-optimize-the-threshold-and-binarization">How to optimize the threshold and binarization</h3>
<p>Start by creating a new file <code>threshold.rs</code> and defining a trait with necessary methods. You need a method to optimise your threshold value and another for performing the binarization operation:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// threshold.rs</span>

<span class="hljs-keyword">pub</span>(<span class="hljs-keyword">crate</span>) <span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">ThresholdingExtensions</span></span> {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">optimize_threshold_for_star_count</span></span>(&amp;<span class="hljs-keyword">self</span>, min_star_count: <span class="hljs-built_in">usize</span>) -&gt; <span class="hljs-built_in">u8</span>;
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">binarize</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, threshold: <span class="hljs-built_in">u8</span>);
}
</code></pre>
<p>Let's implement both of these:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// threshold.rs</span>

<span class="hljs-keyword">use</span> crate::centroid::find_star_centres_and_size;
<span class="hljs-keyword">use</span> crate::StarDetect;

<span class="hljs-keyword">impl</span> ThresholdingExtensions <span class="hljs-keyword">for</span> StarDetect {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">optimize_threshold_for_star_count</span></span>(&amp;<span class="hljs-keyword">self</span>, min_star_count: <span class="hljs-built_in">usize</span>) -&gt; <span class="hljs-built_in">u8</span> {
        <span class="hljs-comment">// Current star count</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> star_count = <span class="hljs-number">0</span>;

        <span class="hljs-comment">// Starting threshold value</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> threshold = <span class="hljs-built_in">u8</span>::MAX;

        <span class="hljs-comment">// Iterate until you've found the best threshold</span>
        <span class="hljs-keyword">while</span> star_count &lt; min_star_count {
            <span class="hljs-comment">// Panic if we reach the 0 intensity value while iterating.</span>
            <span class="hljs-comment">// This means that there are fewer stars than we hoped for.</span>
            <span class="hljs-keyword">if</span> threshold == <span class="hljs-number">0</span> {
                <span class="hljs-built_in">panic!</span>(<span class="hljs-string">"Maximum iteration count reached"</span>);
            }

            <span class="hljs-comment">// Reduce threshold to 95% of its previous value.</span>
            <span class="hljs-comment">// Using this, we check finer and finer differences</span>
            <span class="hljs-comment">// in threshold for each iteration.</span>
            threshold = (<span class="hljs-number">0.95</span> * threshold <span class="hljs-keyword">as</span> <span class="hljs-built_in">f32</span>) <span class="hljs-keyword">as</span> <span class="hljs-built_in">u8</span>;

            <span class="hljs-comment">// Clone the source data since we need to modify it</span>
            <span class="hljs-comment">// without affecting original data.</span>
            <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> source = <span class="hljs-keyword">self</span>.clone();

            <span class="hljs-comment">// Binarize the source data image using current threshold</span>
            ThresholdingExtensions::binarize(&amp;<span class="hljs-keyword">mut</span> source, threshold);

            <span class="hljs-comment">// Find the number of stars detected with the current threshold</span>
            star_count = find_star_centres_and_size(&amp;source.source).len();
        }

        threshold
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">binarize</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, threshold: <span class="hljs-built_in">u8</span>) {
        <span class="hljs-comment">// Iterate over every pixel in source image</span>
        <span class="hljs-keyword">for</span> pixel <span class="hljs-keyword">in</span> <span class="hljs-keyword">self</span>.source.iter_mut() {
            <span class="hljs-keyword">if</span> *pixel &gt; threshold {
                <span class="hljs-comment">// If pixel intensity is greater than threshold</span>
                <span class="hljs-comment">// set it to maximum intensity instead.</span>
                *pixel = <span class="hljs-built_in">u8</span>::MAX;
            } <span class="hljs-keyword">else</span> {
                <span class="hljs-comment">// Otherwise, set it to 0 intensity.</span>
                *pixel = <span class="hljs-number">0</span>;
            }
        }
    }
}
</code></pre>
<p>You might notice that we use the <code>find_star_centres_and_size</code> function when trying to find the optimised threshold value. We'll get to that shortly, as we need to declare some types that will hold the state of our computation before we implement the function.</p>
<p>Create a new file <code>centroid.rs</code>.</p>
<p>Define a new struct that will hold the coordinates and size of the star:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// centroid.rs</span>

<span class="hljs-keyword">use</span> imageproc::point::Point;

<span class="hljs-meta">#[derive(Eq, PartialEq, Copy, Clone, Debug)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">StarCenter</span></span> {
    coord: Point&lt;<span class="hljs-built_in">u32</span>&gt;,
    radius: <span class="hljs-built_in">u32</span>,
}

<span class="hljs-keyword">impl</span> StarCenter {
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">coord</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; &amp;Point&lt;<span class="hljs-built_in">u32</span>&gt; {
        &amp;<span class="hljs-keyword">self</span>.coord
    }
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">radius</span></span>(&amp;<span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">u32</span> {
        <span class="hljs-keyword">self</span>.radius
    }
}
</code></pre>
<p>We've also defined methods to retrieve these fields. <code>Point</code> is a type provided to you by <code>imageproc</code> crate to store coordinates in an image.</p>
<h3 id="heading-how-to-construct-polygons-around-stars">How to construct polygons around stars</h3>
<p>We're going to implement this function inside out. We first need a way to construct our polygon from contours. Let's implement that:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// centroid.rs</span>

<span class="hljs-keyword">use</span> geo::LineString;
<span class="hljs-keyword">use</span> imageproc::contours::Contour;

<span class="hljs-keyword">pub</span>(<span class="hljs-keyword">crate</span>) <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">construct_closed_polygon</span></span>(contour: &amp;Contour&lt;<span class="hljs-built_in">u32</span>&gt;) -&gt; LineString&lt;<span class="hljs-built_in">f32</span>&gt; {
    <span class="hljs-comment">// Create a new line string that connects all points</span>
    <span class="hljs-comment">// in the contour. This can create either an open</span>
    <span class="hljs-comment">// or a closed shape.</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> line_string = LineString::from_iter(contour.points.iter().map(|point| Coord {
        x: point.x <span class="hljs-keyword">as</span> <span class="hljs-built_in">f32</span>,
        y: point.y <span class="hljs-keyword">as</span> <span class="hljs-built_in">f32</span>,
    }));

    <span class="hljs-comment">// If it is an open shape, close the shape to create a</span>
    <span class="hljs-comment">// polygon. This does nothing otherwise.</span>
    line_string.close();

    line_string
}
</code></pre>
<p><code>Contour</code> is a type provided by the <code>imageproc</code> crate, which is what it returns as the result of contouring operation on an image. It contains a list of points that lie on the border of the contour.</p>
<p><code>LineString</code> is a type provided by <code>geo</code> and is defined by them as "An ordered collection of two or more <a target="_blank" href="https://docs.rs/geo/latest/geo/geometry/struct.Coord.html"><code>Coord</code></a>s, representing a path between locations.". In this case, we use this type to construct the polygon shape.</p>
<h3 id="heading-how-to-detect-star-size-and-location-using-contours">How to detect star size and location using contours</h3>
<p>Next, you need a way to compute the <code>StarCenter</code> type we declared previously from contour data:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// centroid.rs</span>

<span class="hljs-keyword">use</span> geo::{Centroid, Coord, EuclideanDistance};

<span class="hljs-keyword">pub</span>(<span class="hljs-keyword">crate</span>) <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">filter_map_contour_to_star_centers</span></span>(contour: &amp;Contour&lt;<span class="hljs-built_in">u32</span>&gt;) -&gt; <span class="hljs-built_in">Option</span>&lt;StarCenter&gt; {
    <span class="hljs-comment">// If there are no points in the contour</span>
    <span class="hljs-comment">// it is not a star.</span>
    <span class="hljs-keyword">if</span> contour.points.is_empty() {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>;
    }

    <span class="hljs-keyword">if</span> contour.points.len() == <span class="hljs-number">1</span> {
        <span class="hljs-comment">// If there's only 1 point in the contour</span>
        <span class="hljs-comment">// consider it to be the center of the star</span>
        <span class="hljs-comment">// of size 1px.</span>
        <span class="hljs-keyword">let</span> center = contour.points.first().unwrap();
        <span class="hljs-keyword">let</span> radius = <span class="hljs-number">1_u32</span>;

        <span class="hljs-keyword">return</span> <span class="hljs-literal">Some</span>(StarCenter {
            coord: *center,
            radius,
        });
    }

    <span class="hljs-comment">// Otherwise, construct a polygon around the star based on</span>
    <span class="hljs-comment">// contour information.</span>
    <span class="hljs-keyword">let</span> polygon = construct_closed_polygon(contour);

    <span class="hljs-comment">// Find the centre of gravity of this polygon (centroid)</span>
    <span class="hljs-keyword">let</span> center = polygon.centroid().unwrap();

    <span class="hljs-comment">// Find the radius of the star based on maximum distance between</span>
    <span class="hljs-comment">// the centroid and any of the points in contour.</span>
    <span class="hljs-keyword">let</span> radius = polygon.points().fold(<span class="hljs-number">0</span>., |distance, point| {
        point.euclidean_distance(&amp;center).max(distance)
    });

    <span class="hljs-comment">// If the radius is less than 1px or more than 24px</span>
    <span class="hljs-comment">// we reject it as a non-star.</span>
    <span class="hljs-keyword">if</span> !(<span class="hljs-number">1</span>. ..=<span class="hljs-number">24</span>.).contains(&amp;radius) {
        <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>;
    }

    <span class="hljs-comment">// Construct star center based on previously computed information</span>
    <span class="hljs-literal">Some</span>(StarCenter {
        coord: Point {
            x: center.x() <span class="hljs-keyword">as</span> <span class="hljs-built_in">u32</span>,
            y: center.y() <span class="hljs-keyword">as</span> <span class="hljs-built_in">u32</span>,
        },
        radius: radius <span class="hljs-keyword">as</span> <span class="hljs-built_in">u32</span>,
    })
}
</code></pre>
<p>This function utilises the <code>construct_closed_polygon</code> function you defined previously to compute the final star centers and sizes. Now for the easy part: let's implement the missing <code>find_star_centres_and_size</code>:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// centroid.rs</span>

<span class="hljs-keyword">use</span> image::GrayImage;

<span class="hljs-keyword">pub</span>(<span class="hljs-keyword">crate</span>) <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">find_star_centres_and_size</span></span>(image: &amp;GrayImage) -&gt; <span class="hljs-built_in">Vec</span>&lt;StarCenter&gt; {
    <span class="hljs-comment">// Compute the contours in source image</span>
    <span class="hljs-keyword">let</span> contours = imageproc::contours::find_contours::&lt;<span class="hljs-built_in">u32</span>&gt;(image);

    contours
        .iter()
        <span class="hljs-comment">// Iterate over all contours and create a list</span>
        <span class="hljs-comment">// of star center and size data.</span>
        .filter_map(filter_map_contour_to_star_centers)
        .collect()
}
</code></pre>
<h3 id="heading-how-to-encapsulate-the-process">How to encapsulate the process</h3>
<p>All you need now is to implement one last method on the <code>StarDetect</code> struct that encapsulates the entire process:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// lib.rs</span>

<span class="hljs-keyword">use</span> crate::centroid::{find_star_centres_and_size, StarCenter};
<span class="hljs-keyword">use</span> crate::threshold::ThresholdingExtensions;

<span class="hljs-keyword">impl</span> StarDetect {
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">find_stars</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>, min_stars: <span class="hljs-built_in">usize</span>) -&gt; <span class="hljs-built_in">Vec</span>&lt;StarCenter&gt; {
        <span class="hljs-keyword">self</span>.extract_small_scale_structures();
        <span class="hljs-keyword">self</span>.apply_noise_reduction();

        <span class="hljs-keyword">let</span> threshold = <span class="hljs-keyword">self</span>.optimize_threshold_for_star_count(min_stars);
        <span class="hljs-keyword">self</span>.binarize(threshold);

        find_star_centres_and_size(&amp;<span class="hljs-keyword">self</span>.source)
    }
}
</code></pre>
<p>This method only calls the functions we've written so far. The user of your library will only need to call this function and nothing else.</p>
<p>You can now use what you've created to find stars in an image. For this article going forward, the image I'll be using to demonstrate is shown below. If you'd like to follow along, you can download the image I'll be using from <a target="_blank" href="https://anshulsanghi-assets.s3.ap-south-1.amazonaws.com/m42-star-detection.jpg"><strong>here</strong></a>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/m42-star-detection.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>M42 Orion Nebula, The Dark Horse Nebula, The Flaming Star Nebula And The Surrounding H-Alpha Gas</em></p>
<p>As you might notice, we have a wide range of star shapes, sizes and colors in this image, but the same goes for noise and other large-scale nebulae structures too.</p>
<h3 id="heading-how-to-test-the-implementation-on-astronomical-images">How to test the implementation on astronomical images</h3>
<p>Create a new file <code>main.rs</code> and declare it as a binary target in the <code>Cargo.toml</code> file. It should look like this:</p>
<pre><code class="lang-toml"><span class="hljs-section">[package]</span>
<span class="hljs-attr">name</span> = <span class="hljs-string">"stardetector"</span>
<span class="hljs-attr">version</span> = <span class="hljs-string">"0.1.0"</span>
<span class="hljs-attr">edition</span> = <span class="hljs-string">"2021"</span>

<span class="hljs-section">[[bin]]</span>
<span class="hljs-attr">name</span> = <span class="hljs-string">"stardetector"</span>
<span class="hljs-attr">path</span> = <span class="hljs-string">"src/main.rs"</span>

<span class="hljs-comment"># See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html</span>

<span class="hljs-section">[dependencies]</span>
<span class="hljs-attr">geo</span> = <span class="hljs-string">"0.28.0"</span>
<span class="hljs-attr">image</span> = <span class="hljs-string">"0.25.1"</span>
<span class="hljs-attr">image-dwt</span> = <span class="hljs-string">"0.3.2"</span>
<span class="hljs-attr">imageproc</span> = <span class="hljs-string">"0.24.0"</span>
</code></pre>
<p>You can finally use the lib we created to process the sample image. The final code in <code>main.rs</code> should look like this:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> image::Rgba;
<span class="hljs-keyword">use</span> stardetector::StarDetect;

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-comment">// Load the image as mutable. You need mutability so that</span>
    <span class="hljs-comment">// you can draw on this image.</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> image = image::open(<span class="hljs-string">"m42-star-detection.jpg"</span>).unwrap();

    <span class="hljs-comment">// Create a new star detector instance. You clone the image</span>
    <span class="hljs-comment">// here because you need to also draw on the image for</span>
    <span class="hljs-comment">// visualisation purposes in this example.</span>
    <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> star_detector = StarDetect::from(image.clone());

    <span class="hljs-comment">// Run the star finder function with a minimum star count of</span>
    <span class="hljs-comment">// 500</span>
    <span class="hljs-keyword">let</span> stars = star_detector.find_stars(<span class="hljs-number">500</span>);

    <span class="hljs-comment">// Iterate over all stars you've found</span>
    <span class="hljs-keyword">for</span> star <span class="hljs-keyword">in</span> stars {
        <span class="hljs-comment">// Draw a hollow circle on the image so that you</span>
        <span class="hljs-comment">// can see what the algorithm found</span>
        imageproc::drawing::draw_hollow_circle_mut(
            &amp;<span class="hljs-keyword">mut</span> image,
            (star.coord().x <span class="hljs-keyword">as</span> <span class="hljs-built_in">i32</span>, star.coord().y <span class="hljs-keyword">as</span> <span class="hljs-built_in">i32</span>),
            <span class="hljs-comment">// Extend the radius by 4px so that it's easier to see</span>
            <span class="hljs-comment">// in the visualisation.</span>
            star.radius() <span class="hljs-keyword">as</span> <span class="hljs-built_in">i32</span> + <span class="hljs-number">4</span>,
            <span class="hljs-comment">// Draw the circle with a pure green color</span>
            Rgba([<span class="hljs-number">0</span>, <span class="hljs-built_in">u8</span>::MAX, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>]),
        );
    }

    <span class="hljs-comment">// Save the image with star positions annotated with</span>
    <span class="hljs-comment">// green circles.</span>
    image.save(<span class="hljs-string">"annotated.jpg"</span>).unwrap();
}
</code></pre>
<p>Ensure that the downloaded image is present at the root of this project folder.</p>
<p>We can finally run the program and see what it gives us:</p>
<pre><code class="lang-shell">cargo run --release
</code></pre>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/annotated.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>A part of the orion region with detected stars annotated with green circles</em></p>
<p>That looks pretty good! If we zoom in to a small part of the image:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/annotated-zoomed.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>A part of the orion region with detected stars annotated with green circles</em></p>
<p>We can see that there are some minor issues with the algorithm, such as stars that are very close to each other and have an overlap of their halos are considered as a single star. The problem is quite an interesting one.</p>
<p>There are various techniques to solve this issue, but they're out of the scope of this article.</p>
<h3 id="heading-how-to-optimize-minimum-star-count">How to optimize minimum star count</h3>
<p>Let's crank up the minimum star count to <strong>1000</strong> and see what happens:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/annotated1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>A part of the orion region with detected stars annotated with green circles</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/annotated1-zoomed.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>A part of the orion region with detected stars annotated with green circles</em></p>
<p>This time, it picked up many of the fainter stars since the threshold had to be lower to accommodate for the higher minimum star count.</p>
<p>It's time to crank it up further! Let's try <strong>2000</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/annotated2.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>A part of the orion region with detected stars annotated with green circles</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/annotated2-zoomed.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>A part of the orion region with detected stars annotated with green circles</em></p>
<p>It picked up even more stars this time, but it has also started hallucinating some stars where there are none. This is being caused by lower threshold retaining more noise in the image, which is then picked up as a star. But noise isn't as visible in the final image unless you really pixel-peep, which is why it appears that the algorithm is hallucinating stars.</p>
<p><strong>Noise</strong>, in this particular situation, not only refers to the noise in the traditional sense – but also to any pixels that do no belong to a star for this particular purpose.</p>
<h3 id="heading-but-there-is-one-more-thing">But there is one more thing...</h3>
<p>Let's crank the minimum star count up to the absolute maximum for this particular image, which I found to be <strong>3500</strong>.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/annotated3.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>A part of the orion region with detected stars annotated with green circles</em></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/annotated3-zoomed.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>A part of the orion region with detected stars annotated with green circles</em></p>
<p>The algorithm now seems to have failed us miserably, which is expected when the noise is too high. There are too many false-positives for this data to be of any use at all.</p>
<p>I wanted to show you this anyway because it shows you the flaws in the algorithm. It also shows you what star detection on noise signal looks like and why we need to pre-process an image to remove everything that isn't a star before we run the star detection.</p>
<p>We're going to stop here for the implementation, but there's many resources you can find below if you're interested in learning more about the topic.</p>
<p>The complete code for everything I talked about today can be found here: <a target="_blank" href="https://github.com/anshulsanghi-blog/stardetector">https://github.com/anshulsanghi-blog/stardetector</a></p>
<h2 id="heading-further-reading"><strong>Further Reading</strong></h2>
<p>These are some of the resources that were very helpful to me when I was trying to figure out how star detection works. The resources are more about plate-solving (the process of figuring out the exact coordinates in the night sky of things in an image), but star detection is a crucial part of that process.</p>
<ul>
<li><a target="_blank" href="https://olegignat.com/how-plate-solving-works/">How astronomic plate-solving works</a> by Oleg Ignat</li>
<li><a target="_blank" href="https://pixinsight.com/doc/tools/StarAlignment/StarAlignment.html#description_002">Star Detection during StarAlignment Process In PixInsight</a></li>
</ul>
<p>Stars have pretty interesting characteristics, some of which are unique such as their <a target="_blank" href="https://en.wikipedia.org/wiki/Point_spread_function">point-spread function</a> estimates. These characteristics can be implemented to further improve the star detection and filtering of false-positives.</p>
<p>In addition, I've created a Rust library that implements this algorithm, but has some additional features already, and more robust processes are in the works.</p>
<p>Things such as handling RGB images properly instead of converting them to grayscale are already implemented. It also has the ability to work with RAW images.</p>
<p>I'm also soon going to be working on performance improvements for the same.</p>
<p>If you want to learn more, or contribute to the library, feel free to do so. The repository can be found here: <a target="_blank" href="https://github.com/anshap1719/stardetect">https://github.com/anshap1719/stardetect</a>  </p>
<h2 id="heading-wrapping-up"><strong>Wrapping Up</strong></h2>
<p>I hope you enjoyed the journey so far. If image processing and analysis techniques or their implementation in Rust is something that interests you, then stay tuned for more as these are the topics I love writing about.</p>
<p>Also, feel free to <strong><a target="_blank" href="mailto:contact@anshulsanghi.tech">contact me</a></strong> if you have any questions or opinions on this topic.</p>
<h3 id="heading-enjoying-my-work"><strong>Enjoying my work?</strong></h3>
<p>Consider buying me a coffee to support my work!</p>


<p>Till next time, happy coding and wishing you clear skies!</p>
 ]]>
                </content:encoded>
            </item>
        
            <item>
                <title>
                    <![CDATA[ Rust Tutorial – Learn Multi-Scale Processing of Astronomical Images ]]>
                </title>
                <description>
                    <![CDATA[ Recently, there's been a massive amount of effort put into developing novel image processing techniques. And many of them are derived from digital signal processing methods such as Fourier and Wavelet transforms.  These techniques have not only enabl... ]]>
                </description>
                <link>https://www.freecodecamp.org/news/multi-scale-analysis-of-images-in-rust/</link>
                <guid isPermaLink="false">66bb5795f55324ca867c88e2</guid>
                
                    <category>
                        <![CDATA[ algorithms ]]>
                    </category>
                
                    <category>
                        <![CDATA[ image processing ]]>
                    </category>
                
                    <category>
                        <![CDATA[ Rust ]]>
                    </category>
                
                <dc:creator>
                    <![CDATA[ Anshul Sanghi ]]>
                </dc:creator>
                <pubDate>Wed, 10 Apr 2024 15:48:11 +0000</pubDate>
                <media:content url="https://www.freecodecamp.org/news/content/images/2024/04/Watermark.png" medium="image" />
                <content:encoded>
                    <![CDATA[ <p>Recently, there's been a massive amount of effort put into developing novel image processing techniques. And many of them are derived from digital signal processing methods such as Fourier and Wavelet transforms. </p>
<p>These techniques have not only enabled a wide range of image processing techniques such as noise reduction, sharpening, and dynamic-range extension, but have also enabled many techniques used in compute vision such as edge detection, object detection, and so on.</p>
<p>Multi-scale analysis is one of the newer techniques (relatively speaking) that has been adopted in a wide range of applications, especially in the astronomical image and data processing applications. This technique, which is based on Wavelet transform, allows us to divide our data into multiple signals, that all add up to make the final signal. </p>
<p>We can then perform our processing or analysis work on this individual sub-signals, allowing us to do targeted operations that do not affect other sub-signals. </p>
<p>In this tutorial, we'll first be exploring what the technique is all about, through the lens of a particular algorithm for performing multi-scale analysis on images. We'll then move on to looking at how we can implement what we discussed in the first part in Rust programming language and recreate the examples you see in the first half of the article.</p>
<h2 id="heading-before-you-read">Before You Read:</h2>
<h3 id="heading-prerequisites-for-part-1">Prerequisites for Part 1:</h3>
<p>The technique described is derived from the concept of "Wavelet Transforms". You don't need to know everything about it, but a very basic understanding will help you grasp the material better.</p>
<p>Since the article focuses on image processing and analysis, a basic understanding of how pixels work in digital format is helpful, but not mandatory.</p>
<h3 id="heading-prerequisites-for-part-2">Prerequisites for Part 2:</h3>
<p>Here, we focus on implementing the algorithm using the Rust programming language, without going much into the details of the language itself. So being comfortable writing Rust programs, and comfortable reading crate documentations is required.</p>
<p>If this is not you, you can still read Part 1 and learn the technique, and then maybe you'll want to then try it out in a language of your choice. If you're not familiar with Rust, I highly encourage you to learn the basics. <a target="_blank" href="https://www.freecodecamp.org/news/rust-in-replit/">Here's an interactive Rust course</a> that can get you started.</p>
<h2 id="heading-table-of-contents">Table Of Contents</h2>
<ol>
<li><a class="post-section-overview" href="#heading-part-1-understanding-the-multi-scale-processing-technique-and-algorithm">Part 1: Understanding Multi-Scale Processing Technique And Algorithm</a><ol>
<li><a class="post-section-overview" href="#heading-what-is-multi-scale-image-processing">What is multi-scale image processing</a></li>
<li><a class="post-section-overview" href="#heading-the-a-trous-wavelet-transform">The <em>À Trous</em> Wavelet Transform</a></li>
<li><a class="post-section-overview" href="#heading-scaling-functions">Scaling Functions</a></li>
<li><a class="post-section-overview" href="#heading-convolution-pixels-at-each-scale">Convolution Pixels At Each Scale</a></li>
<li><a class="post-section-overview" href="#heading-handling-boundary-conditions">Handling Boundary Conditions</a> </li>
<li><a class="post-section-overview" href="#heading-computing-maximum-possible-scales-for-any-given-image">Computing Maximum Possible Scales For Any Given Image</a></li>
<li><a class="post-section-overview" href="#heading-closing-notes">Closing Notes</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#heading-part-2-how-to-implement-a-trous-tranform-in-rust">Part 2: How to Implement <em>À Trous</em> Tranform in Rust</a><ol>
<li><a class="post-section-overview" href="#heading-prerequisites">Prerequisites</a></li>
<li><a class="post-section-overview" href="#heading-the-a-trous-transform">The <em>À Trous</em> Transform</a></li>
<li><a class="post-section-overview" href="#heading-iterators-and-the-a-trous-transform">Iterators And The <em>À Trous</em> Transform</a></li>
<li><a class="post-section-overview" href="#heading-convolution">Convolution</a></li>
<li><a class="post-section-overview" href="#heading-implementing-the-iterator">Implementing the Iterator</a></li>
<li><a class="post-section-overview" href="#heading-recomposition">Recomposition</a></li>
<li><a class="post-section-overview" href="#heading-using-the-a-trous-transform">Using The <em>À Trous</em> Transform</a></li>
<li><a class="post-section-overview" href="#heading-further-reading">Further Reading</a></li>
</ol>
</li>
<li><a class="post-section-overview" href="#heading-wrapping-up">Wrapping Up</a></li>
</ol>
<h2 id="heading-part-1-understanding-the-multi-scale-processing-technique-and-algorithm">Part 1: Understanding the Multi-Scale Processing Technique and Algorithm</h2>
<p>So what do we mean when we talk about multi-scale processing or analysis of some data? Well, we usually mean breaking down the input data into multiple signals, each representing a particular scale of information. </p>
<p>Scale, when talking about image analysis, simply refers to the size of structures that we are looking at at any given time. It ignores everything else that's either smaller or larger than the current scale.</p>
<h3 id="heading-what-is-multi-scale-image-processing">What is multi-scale image processing?</h3>
<p>For images, "scales" generally refer to the size in pixels of various structures or details in the image. You'll be able to get an intuitive understanding by looking at the following example:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/Processed.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Messier 33, AKA Triangulum Galaxy</em></p>
<p>Assuming our naïve understanding is correct, we can derive images of at-least the following 3 scales:</p>
<ul>
<li>Very small structures, usually the size of a single pixel. This layer, when separated from the rest of the image, will only contain the noise and some sharp stars for the most part.</li>
<li>Small structures, usually a few pixels in size. This layer, when separated, will contain all of the stars and the very fine details in the galaxy arms.</li>
<li>Large and very large scale structures, usually 100s of pixels in size. This layer, when separated, will contain the general size and shape of the galaxy at the center.</li>
</ul>
<p>Now the question becomes, <strong>why do we need to do all of this in the first place?</strong></p>
<p>The answer is simple: it allows us to make targeted enhancements and changes to an image. </p>
<p>For example, noise reduction on the overall image will usually result in a loss of sharpness in the galaxy. But since we have broken our image down into multiple scales, we can easily apply noise reduction to only the first few layers, as most of the random noise that is easy to remove resides only in lower scale layers. </p>
<p>We then re-combine the noise-reduced low-scale layers with unmodified large-scale ones, and we have an output that gives us noise reduction without a loss in quality.</p>
<p>Another peculiar thing about noise is that it's almost always present in just one of these layers, making noise reduction process both easy and non-destructive.</p>
<p>If you're more of a visual learner, let's see this in practice using the image we used above. We're gonna be working with the following grayscale version of that image, where I've also added random gaussian noise:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/m33-noise-lum-1.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Messier 33 AKA Triangulum Galaxy, Converted to grayscale and with added Gaussian noise</em></p>
<p>Performing scale-based layer separation on this image, we get the following results. Note that the results are rescaled to a range where they can be viewed as an image for representational purpose. The actual transform produces pixel values that don't make sense when looked at independently, but all of the techniques and calculations described in this tutorial can still be safely applied without rescale. The recomposition process automatically gives us back the correct range:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/trous-decomposition..jpg" alt="Image" width="600" height="400" loading="lazy">
<em>9-level À Trous Decomposition. From top-left to bottom-right, we have images at the following pixel scales: 1, 2 4, 8, 16, 32, 64, 128, 256 (powers of 2)</em></p>
<ol>
<li>The first and second layers contain the noise and stars. In this particular example, noise is mixed in with the stars. But using the first and second layers, we can easily target areas that are not present in the second layer, as we can be sure that those are where the noise is present in the first layer.</li>
<li>With the third layer, we still see the residue luminance from stars. But if you look closely, we also see very faintly the arms of the galaxy starting to appear.</li>
<li>From the fourth layer onwards, we see the galaxy at varying scales and detail levels, completely without the stars. We start with the finer details (relatively small scale details) and increasingly move on to larger and larger scale samples. By the end, we only see a vague shape where the galaxy used to be.</li>
</ol>
<p>From here on, we can selectively apply noise reduction to the first two layers. Then we can recombine all of the layers to create the following image that has very little noise while preserving the same amount of details in the stars and the galaxy arms:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/wavelet-processed.jpg" alt="Image" width="600" height="400" loading="lazy">
<em>Messier 33 AKA Triangulum Galaxy, result of recombining all layers but with noise reduction applied to the pixel scale 1 &amp; 2 layers</em></p>
<p>In its most basic form, multi-scale analysis involves breaking up your source image, commonly referred to as the "signal", into multiple "signals" – each containing the data for a particular scale in the source signal. </p>
<p>Scale, when talking about image signal here, refers to the distance between adjacent pixels that we take when creating the layer from the source image.</p>
<p>In practice, this technique is used as the one of the first steps in all kinds of astronomical data analysis and image processing. </p>
<p>As an example, you can use the technique to detect locations of stars while ignoring larger structures much more easily than would be possible otherwise.</p>
<h3 id="heading-the-a-trous-wavelet-transform">The <em>À Trous</em> Wavelet Transform</h3>
<p>All of what I've showed you previously, and all of what you're going to see in this tutorial, was achieved with wavelet decomposition and recomposition using the à <em>trous</em> algorithm for discreet wavelet transforms.</p>
<p>This algorithm has been used throughout the years for various applications. But it's become particularly important recently in astronomical image processing applications, where different objects and signals in an image can be completely separated based on structural scales.</p>
<p>Here's how the algorithm works:</p>
<ol>
<li>We start with the source image input and number of levels to decompose into n.</li>
<li>For each level n:<ul>
<li>We convolve the image with our scaling function (we'll see what this is in a bit), where adjacent pixels are considered to be <strong>2<sup>n</sup></strong> units apart from each other, giving us the result <strong>result<sub>n</sub></strong>. This is where the "À Trous" name comes from, which literally translates to "with holes".</li>
<li>The layer output <strong>output<sub>n</sub></strong> is then computed using <strong>input</strong> - <strong>result<sub>n</sub></strong>.</li>
<li>We then update <strong>input</strong> to equal <strong>result<sub>n</sub></strong>. This is also known as residue data which serves as the source data for next layer.</li>
</ul>
</li>
<li>Repeat the above steps for all levels.</li>
<li>In the end, we have 9 wavelet layers, and 1 residue layer. All 10 layers are required for the recomposition.</li>
</ol>
<p>For a more mathematical approach to understanding this algorithm, I encourage you to read about <a target="_blank" href="https://www.eso.org/sci/software/esomidas/doc/user/18NOV/volb/node317.html"><strong>the à trous algorithm here</strong></a><strong>.</strong> </p>
<p>The recomposition process is very straightforward: we just need to add all 10 layers together. We can chose to apply positive or negative <strong>bias</strong> to any of the layers, which is a factor by which to multiply the layer pixel values during recomposition. You can use it either to enhance or diminish the characteristics of that particular layer.</p>
<h3 id="heading-scaling-functions">Scaling Functions</h3>
<p>Scaling functions are specific <a target="_blank" href="https://en.wikipedia.org/wiki/Kernel_(image_processing)">convolution kernels</a> that help us better represent data at a particular scale based on our use case. There are 3 most commonly used scaling functions, which are shown below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/b3spline-level2.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/linear-level2.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/08/low-scale-level2.jpg" alt="Image" width="600" height="400" loading="lazy"></p>
<p>The images above show the 3 most commonly used scaling functions in the À Trous algorithm, visualised using 3rd level decomposition of the triangulum galaxy image used previously:</p>
<ul>
<li>B3 Spline is a very smooth kernel. It is mostly used in isolation of large scale structures. If we wanted to sharpen our galaxy, we would have used this kernel.</li>
<li>Low-scale is a very sharply peaked kernel, and is best at working with small scale structures.</li>
<li>Linear interpolation kernel gives us the best of both worlds, and hence is used when we need to work with both small scale and large scale structures. This is what we have used in all of our previous examples.</li>
</ul>
<h3 id="heading-convolution-pixels-at-each-scale">Convolution Pixels At Each Scale</h3>
<p>I mentioned in the algorithm that at each scale, the pixels in the image are considered to be 2<sup>n</sup> units apart. Let's try to grasp a better understanding of this using the following visualisation:</p>
<p>Consider the following 8px by 8px image. Each pixel is labeled 1 through 64, which is their index.</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/1-32x32mm.png" alt="Image" width="600" height="400" loading="lazy">
<em>A representational pixel grid of a 8x8px image</em></p>
<p>We're going to focus on a convolution operation of one of the center pixels only for this example, let's say pixel number 28.</p>
<p><strong>Scale 0:</strong> At scale 0, the value of 2<sup>n</sup> becomes <strong>1</strong>. This means that for convolution, we'll consider pixels that are 1 unit apart from our target center pixel. These pixels are highlighted below:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/scale0-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>8x8px grid with pixels that are involved in convolution for pixel number 28 highlighted at scale 0</em></p>
<p><strong>Scale 1:</strong> This is where things get interesting. At scale 1, the value of 2<sup>n</sup> becomes <strong>2</strong>. This means that for convolution, we'll jump directly to pixels that are 2 locations apart from the target pixel:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/scale1-3.png" alt="Image" width="600" height="400" loading="lazy">
<em>8x8px grid with pixels that are involved in convolution for pixel number 28 highlighted at scale 1</em></p>
<p>As you can see, we've created "holes" in our computation of the value of the target pixel by skipping <strong>2<sup>n</sup> - 1</strong> adjacent pixels and selecting the <strong>2<sup>n</sup>th</strong> pixel. This is the basis of the algorithm.</p>
<p>This process is repeated for every pixel in the image, just like a regular convolution process. And each time, we consider increasing distances between pixels for computation of final values at increasing scales. </p>
<p>Let's look at just one more scale.</p>
<p><strong>Scale 2</strong>: This is where things get even more interesting. At scale 2 the value of 2<sup>n</sup> becomes <strong>4</strong>. This means that for convolution, we'll jump directly to pixels that are <strong>4</strong> locations apart from the target pixel:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/scale2-1.png" alt="Image" width="600" height="400" loading="lazy">
<em>8x8px grid with pixels that are involved in convolution for pixel number 28 highlighted at scale 2</em></p>
<p>Wait what? Why are we choosing pixels 1, 4, 8, 25, &amp; 57? 1 &amp; 4 are only 3 locations apart, 25 is only 2 locations apart, and 8 &amp; 57 are not even diagonally aligned with the target pixel. What's going on?</p>
<h3 id="heading-handling-boundary-conditions">Handling Boundary Conditions</h3>
<p>As we've mentioned that this process is executed for all of the pixels in an image, we also need to consider cases where the pixel locations for convolution lie outside of the image.</p>
<p>This is not a concept unique to this algorithm. During convolution, this is referred to as a boundary condition or handling boundary pixels. There are various techniques for dealing with this, and all of them involve virtually extending the image in order to make it seem like we're not encountering the boundary at all.</p>
<p>Some of the techniques are:</p>
<ul>
<li>Extending as much as needed by copying the value of the last row/column</li>
<li>Mirroring the image on all edges and corners</li>
<li>Wrapping the image around the edges.</li>
</ul>
<p>In our example, we're employing the "mirroring" technique. When implementing such an algorithm, we don't need to actually create an extended image. Any boundary handling is implementable using just basic mathematical formulae.</p>
<p>Our extended image, with the correct pixels selected for scale 2, is as follows:</p>
<p><img src="https://www.freecodecamp.org/news/content/images/2024/04/scale2-mirrored.png" alt="Image" width="600" height="400" loading="lazy">
<em>Source image extended on all edges and corners using the mirroring technique. All of the faded regions represent extended areas.</em></p>
<p>Again, the extension is only logical and is completely computed using formulae, as opposed to actually extending the source image and then checking. We can easily see that with the mirrored images in place, our basic rule of picking pixels that are 2<sup>n</sup> locations apart is still followed.</p>
<h3 id="heading-computing-maximum-possible-scales-for-any-given-image">Computing Maximum Possible Scales for Any Given Image</h3>
<p>If you think about it carefully, you'll see that the maximum layers an image can be decomposed into can be calculated by computing the log<sub>2</sub> of the image width or height (whichever is lower) and throwing away the fractional part. </p>
<p>In our 5x5 image, log<sub>2</sub>(5) ~= <strong>2.32</strong>. If we throw away the fractional part, that leaves us with 2 layers. Similarly, for a 1000x1000px image, log<sub>2</sub>1000 ~= <strong>9.96</strong>, which means we can decompose a 1000x1000 px image into a maximum of 9 layers. It simply implies that our "holes" cannot be larger than the width or height.</p>
<p>Even with the mirroring extension we used above, if the holes are larger than the width of the image, they'll still end up outside of the extended regions, specially for corner or boundary pixels, making it impossible to perform convolution at that scale.</p>
<h3 id="heading-closing-notes">Closing Notes</h3>
<p>Thinking about the examples and visualisations a bit more, you can clearly see how and why this algorithm works, and how it's able to separate out structures in an image based on their sizes. The increasing hole sizes make it so that only structures larger than the hole itself are retained for any given layer.</p>
<p>A big advantage of using this algorithm is the computational cost. Since this doesn't involve Fourier or Wavelet transforms, the computational cost is quite low, relatively speaking. The memory cost, however, is indeed higher. But more often than not that is a good tradeoff.</p>
<p>Another advantage of this algorithm when comparing it to other discreet wavelet transform algorithms is that the size of source image is preserved throughout the entire process. There's no decimation or upscaling happening here, making this algorithm one of the easiest ones to understand and implement.</p>
<p>The algorithm is used in almost all of the astronomical image processing softwares such as <a target="_blank" href="https://pixinsight.com/">PixInsight</a>, <a target="_blank" href="https://siril.org/">Siril</a>, and many others.</p>
<p>This algorithm is also known by other names such as <strong>Stationary Wavelet Transform</strong> and <strong>Starlet Transform</strong>.</p>
<h2 id="heading-part-2-how-to-implement-a-trous-tranform-in-rust">Part 2: How to Implement <em>À Trous</em> Tranform in Rust</h2>
<p>Now I'm going to show you how you can implement this algorithm in Rust. </p>
<p>For the purposes of this tutorial, I'm going to assume that you're pretty familiar with Rust and its basic concepts, such as data-types, iterators, and traits and are comfortable writing programs that use these concepts. </p>
<p>I'm also going to assume that you have an understanding of what convolution and convolution kernels mean in this context.</p>
<h3 id="heading-prerequisites">Prerequisites</h3>
<p>We're going to need a couple of dependencies. Before we get to that, let's quickly create a new project:</p>
<pre><code class="lang-shell">cargo new --lib atrous-rs
cd atrous-rs
</code></pre>
<p>Now let's all of the dependencies we need. We actually only need 2:</p>
<pre><code class="lang-shell">cargo add image ndarray
</code></pre>
<p><strong><code>image</code></strong> is a Rust library we'll use to work with images of all of the standard formats and encodings. It also helps us convert between various formats, and provides easy access to pixel data as buffers.</p>
<p><strong><code>ndarray</code></strong> is a Rust library that helps you you create, manipulate, and work with 2D, 3D, or N-Dimensional arrays. We can use nested Vectors, but using a project like ndarray is better in this case because we need to perform a lot of operations on both individual values as well as their neighbours. Not only is it much easier to do with <strong>ndarray</strong>, but they also have performance optimisations built in for many operations and CPU types.</p>
<p>Although I'll be covering the basic functions/traits/methods/data-types we use from these crates, I'm not going to go into too much detail for them. I encourage you to read the docs instead.</p>
<p>We're actually going to jump straight to algorithm implementation, and come back later to see how we can use it.</p>
<h3 id="heading-the-a-trous-transform">The <em>À Trous</em> Transform</h3>
<p>Create a new file that will hold our implementation. Let's name it <code>transform.rs</code>.</p>
<p>Start with adding the following struct, that will hold the information we need to perform the transform:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// transform.rs</span>

<span class="hljs-keyword">use</span> ndarray::Array2;

<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">ATrousTransform</span></span> {
    input: Array2&lt;<span class="hljs-built_in">f32</span>&gt;, <span class="hljs-comment">// `Array2&lt;f32&gt;` is a 2D array where each value is of type `f32`. This will hold our pixel data for input image.</span>
    levels: <span class="hljs-built_in">usize</span>, <span class="hljs-comment">// The number of levels or scales to decompose the image into</span>
    current_level: <span class="hljs-built_in">usize</span>, <span class="hljs-comment">// Current level that we need to generate. This holds the state of our iterator.</span>
    width: <span class="hljs-built_in">usize</span>, <span class="hljs-comment">// Width of input image</span>
    height: <span class="hljs-built_in">usize</span>, <span class="hljs-comment">// Height of input image</span>
}
</code></pre>
<p>We also need a way to create this struct easily. In our case, we want to be able to create it from the input image directly. Also, input image can be of any of the supported format and encoding, but we want a consistent color-type to implement the calculations, so we'll also need to convert the image to our expected format.</p>
<p>It's helpful to extract all of this logic away using the "constructor" pattern in Rust. Let's implement that:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// transform.rs</span>

<span class="hljs-keyword">use</span> image::GenericImageView;

<span class="hljs-keyword">impl</span> ATrousTransform {
    <span class="hljs-keyword">pub</span> <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">new</span></span>(input: &amp;image::DynamicImage, levels: <span class="hljs-built_in">usize</span>) -&gt; <span class="hljs-keyword">Self</span> {
        <span class="hljs-keyword">let</span> (width, height) = input.dimensions();
        <span class="hljs-keyword">let</span> (width, height) = (width <span class="hljs-keyword">as</span> <span class="hljs-built_in">usize</span>, height <span class="hljs-keyword">as</span> <span class="hljs-built_in">usize</span>);

        <span class="hljs-comment">// Create a new 2D array with proper size for each dimension to hold all of our input's pixel data. Method `zeros` takes a "shape" parameter, which is a tuple of (rows_count, columns_count).</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> data = Array2::&lt;<span class="hljs-built_in">f32</span>&gt;::zeros((height, width));

        <span class="hljs-comment">// Convert the image to be a grayscale image where each pixel value is of type `f32`. Loop over all pixels in the input image along with its 2D location.</span>
        <span class="hljs-keyword">for</span> (x, y, pixel) <span class="hljs-keyword">in</span> input.to_luma32f().enumerate_pixels() {
            <span class="hljs-comment">// Put the pixel value at appropriate location in our data array. The `[[]]` syntax is used to provide a 2-dimensional index such as `[[row_index, col_index]]`</span>
            data[[y <span class="hljs-keyword">as</span> <span class="hljs-built_in">usize</span>, x <span class="hljs-keyword">as</span> <span class="hljs-built_in">usize</span>]] = pixel.<span class="hljs-number">0</span>[<span class="hljs-number">0</span>];
        }

        <span class="hljs-keyword">Self</span> {
            input: data,
            levels,
            current_level: <span class="hljs-number">0</span>,
            width,
            height
        }
    }
}
</code></pre>
<p>This takes care of converting the image to grayscale and converting the pixel values to <code>f32</code>. If you're not already aware, for images with floating-point pixel values, the values are always normalized. This means that they are always between 0 and 1 – 0 representing black and 1 representing white.</p>
<h3 id="heading-iterators-and-the-a-trous-transform">Iterators and the <em>À Trous</em> Transform</h3>
<p>Before we continue, let's think about the algorithm for a second. We need to be able to generate images at increasing scales, until we hit the maximum number of levels we need. </p>
<p>We want the consumer of our library to have access to all of these scales, and be able to manipulate them and also easily recombine once they're done. They need to be able to filter layers to ignore structures at certain scales, manipulate or "map" them to change their characteristics, perform operations on them, or even store each image if they so need.</p>
<p>This sounds an awful lot like Iterators! Iterators give us methods like <code>filter</code>, <code>skip</code>, <code>take</code>, <code>map</code>, <code>for_each</code>, and so on, all of which are exactly all we need to work with our layers before recomposition.</p>
<p>One added advantage of Iterators is that it allows you to finish processing each layer all the way through before you move on to the next one. If you're unsure why this is, I suggest reading more about <a target="_blank" href="https://doc.rust-lang.org/book/ch13-02-iterators.html">processing a series of items with Iterators in Rust</a>.</p>
<p>We're going implement the <code>Iterator</code> trait for our <code>ATrousTransform</code> type which should produce a wavelet layer as output for each iteration. </p>
<p>We're going to be implementing the inner-most parts of the algorithm first, and build out from there. So we first need a way to convolve an input data buffer with the scaling function while making sure that adjacent pixels are 2<sup>n</sup> locations apart, which is the first step in our loop.</p>
<h3 id="heading-convolution">Convolution</h3>
<p>We need to define our convolution kernel before we can do anything else. Create a new file <code>kernel.rs</code> and add it to <code>lib.rs</code> with the following contents:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// kernel.rs</span>

<span class="hljs-meta">#[derive(Copy, Clone)]</span>
<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">LinearInterpolationKernel</span></span> {
    values: [[<span class="hljs-built_in">f32</span>; <span class="hljs-number">3</span>]; <span class="hljs-number">3</span>]
}

<span class="hljs-keyword">impl</span> <span class="hljs-built_in">Default</span> <span class="hljs-keyword">for</span> LinearInterpolationKernel {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">default</span></span>() -&gt; <span class="hljs-keyword">Self</span> {
        <span class="hljs-keyword">Self</span> {
            values: [
                [<span class="hljs-number">1</span>. / <span class="hljs-number">16</span>., <span class="hljs-number">1</span>. / <span class="hljs-number">8</span>., <span class="hljs-number">1</span>. / <span class="hljs-number">16</span>.],
                [<span class="hljs-number">1</span>. / <span class="hljs-number">8</span>., <span class="hljs-number">1</span>. / <span class="hljs-number">4</span>., <span class="hljs-number">1</span>. / <span class="hljs-number">8</span>.],
                [<span class="hljs-number">1</span>. / <span class="hljs-number">16</span>., <span class="hljs-number">1</span>. / <span class="hljs-number">8</span>., <span class="hljs-number">1</span>. / <span class="hljs-number">16</span>.],
            ]
        }
    }
}
</code></pre>
<p>We define it using a struct instead of a constant array of arrays because we need to define some tiny helpful methods on it related to index handling. We'll come back to that later.</p>
<p>Create another file <code>convolve.rs</code>. This is where all of the code for handling convolution for individual pixels will go. We'll define a <code>Convolution</code> trait that will define methods needed to perform the convolution on every pixel in current layer.</p>
<pre><code class="lang-rust"><span class="hljs-comment">// convolve.rs</span>

<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">Convolution</span></span> {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">compute_pixel_index</span></span>(
        &amp;<span class="hljs-keyword">self</span>,
        distance: <span class="hljs-built_in">usize</span>,
        kernel_index: [<span class="hljs-built_in">isize</span>; <span class="hljs-number">2</span>],
        target_pixel_index: [<span class="hljs-built_in">usize</span>; <span class="hljs-number">2</span>]
    ) -&gt; [<span class="hljs-built_in">usize</span>; <span class="hljs-number">2</span>];

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">compute_convoluted_pixel</span></span>(
        &amp;<span class="hljs-keyword">self</span>, 
        distance: <span class="hljs-built_in">usize</span>, 
        index: [<span class="hljs-built_in">usize</span>; <span class="hljs-number">2</span>]
    ) -&gt; <span class="hljs-built_in">f32</span>;
}
</code></pre>
<p>You may ask why we need a trait here instead of a simple <code>impl</code> block. We are only working with Grayscale images in this article, but you may want to extend it to implement it for RGB or other color modes as well.</p>
<p>Now, you need to implement this trait for your <code>ATrousTransform</code> struct:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// convolve.rs</span>

<span class="hljs-keyword">impl</span> Convolution <span class="hljs-keyword">for</span> ATrousTransform {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">compute_pixel_index</span></span>(
        &amp;<span class="hljs-keyword">self</span>, 
        distance: <span class="hljs-built_in">usize</span>, 
        kernel_index: [<span class="hljs-built_in">isize</span>; <span class="hljs-number">2</span>], 
        target_pixel_index: [<span class="hljs-built_in">usize</span>; <span class="hljs-number">2</span>]
    ) -&gt; [<span class="hljs-built_in">usize</span>; <span class="hljs-number">2</span>] {
        <span class="hljs-keyword">let</span> [kernel_index_x, kernel_index_y] = kernel_index;

        <span class="hljs-comment">// Compute the actual distance of adjacent pixel</span>
        <span class="hljs-comment">// by multiplying their relative position with the</span>
        <span class="hljs-comment">// size of the hole.</span>
        <span class="hljs-keyword">let</span> x_distance = kernel_index_x * distance <span class="hljs-keyword">as</span> <span class="hljs-built_in">isize</span>;
        <span class="hljs-keyword">let</span> y_distance = kernel_index_y * distance <span class="hljs-keyword">as</span> <span class="hljs-built_in">isize</span>;

        <span class="hljs-keyword">let</span> [x, y] = target_pixel_index;

        <span class="hljs-comment">// Compute the index of adjacent pixel in the 2D</span>
        <span class="hljs-comment">// image based on the index of current pixel.</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> x = x <span class="hljs-keyword">as</span> <span class="hljs-built_in">isize</span> + x_distance;
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> y = y <span class="hljs-keyword">as</span> <span class="hljs-built_in">isize</span> + y_distance;

        <span class="hljs-comment">// If x index is out of bounds, consider x to be</span>
        <span class="hljs-comment">// the nearest boundary location</span>
        <span class="hljs-keyword">if</span> x &lt; <span class="hljs-number">0</span> {
            x = <span class="hljs-number">0</span>;
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> x &gt; <span class="hljs-keyword">self</span>.width <span class="hljs-keyword">as</span> <span class="hljs-built_in">isize</span> - <span class="hljs-number">1</span> {
            x = <span class="hljs-keyword">self</span>.width <span class="hljs-keyword">as</span> <span class="hljs-built_in">isize</span> - <span class="hljs-number">1</span>;
        }

        <span class="hljs-comment">// If y index is out of bounds, consider y to be</span>
        <span class="hljs-comment">// the nearest boundary location</span>
        <span class="hljs-keyword">if</span> y &lt; <span class="hljs-number">0</span> {
            y = <span class="hljs-number">0</span>;
        } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> y &gt; <span class="hljs-keyword">self</span>.height <span class="hljs-keyword">as</span> <span class="hljs-built_in">isize</span> - <span class="hljs-number">1</span> {
            y = <span class="hljs-keyword">self</span>.height <span class="hljs-keyword">as</span> <span class="hljs-built_in">isize</span> - <span class="hljs-number">1</span>;
        }

        <span class="hljs-comment">// The final 2D index of pixel.</span>
        [y <span class="hljs-keyword">as</span> <span class="hljs-built_in">usize</span>, x <span class="hljs-keyword">as</span> <span class="hljs-built_in">usize</span>]
    }

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">compute_convoluted_pixel</span></span>(
        &amp;<span class="hljs-keyword">self</span>, 
        distance: <span class="hljs-built_in">usize</span>, 
        [x, y]: [<span class="hljs-built_in">usize</span>; <span class="hljs-number">2</span>]
    ) -&gt; <span class="hljs-built_in">f32</span> {
        <span class="hljs-comment">// Create new variable to hold the result of convolution</span>
        <span class="hljs-comment">// for current pixel.</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> pixels_sum = <span class="hljs-number">0.0</span>;

        <span class="hljs-keyword">let</span> kernel = LinearInterpolationKernel::default();

        <span class="hljs-comment">// Iterate over relative position of pixels from the center</span>
        <span class="hljs-comment">// pixel to perform convolution with. In other words, </span>
        <span class="hljs-comment">// these are the indexes of neighbouring pixels from the</span>
        <span class="hljs-comment">// center pixel.</span>
        <span class="hljs-keyword">for</span> kernel_index_x <span class="hljs-keyword">in</span> -<span class="hljs-number">1</span>..=<span class="hljs-number">1</span> {
            <span class="hljs-keyword">for</span> kernel_index_y <span class="hljs-keyword">in</span> -<span class="hljs-number">1</span>..=<span class="hljs-number">1</span> {
                <span class="hljs-comment">// Get the computed pixel location that maps to</span>
                <span class="hljs-comment">// the current position in kernel</span>
                <span class="hljs-keyword">let</span> pixel_index = <span class="hljs-keyword">self</span>.compute_pixel_index(
                    distance,
                    [kernel_index_x, kernel_index_y],
                    [x, y]
                );

                <span class="hljs-comment">// Get the multiplicative factor (kernel value) for </span>
                <span class="hljs-comment">// this relative location from the kernel.</span>
                <span class="hljs-keyword">let</span> kernel_value = kernel.value_from_relative_index(
                    kernel_index_x,
                    kernel_index_y
                );

                <span class="hljs-comment">// Multiply the pixel value with kernel scaling</span>
                <span class="hljs-comment">// factor and add it to the pixel sum.</span>
                pixels_sum += kernel_value * <span class="hljs-keyword">self</span>.input[pixel_index];
            }
        }

        <span class="hljs-comment">// Return the value of computed pixel from convolution process.</span>
        pixels_sum
    }
}
</code></pre>
<p>We need to do computations to figure out each pixel's location based on the relative position in the kernel from the center pixel as well as ensure that the "hole size" is also being taken into consideration for the final pixel index. As you might notice, you also want to handle the boundary conditions when computing indexes.</p>
<p>I encourage to take your time here and go through the code and the comments.</p>
<h3 id="heading-implementing-the-iterator">Implementing the Iterator</h3>
<p>It's finally time to implement the <code>Iterator</code> trait for your <code>ATrousTransform</code>:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// transform.rs</span>

<span class="hljs-keyword">impl</span> <span class="hljs-built_in">Iterator</span> <span class="hljs-keyword">for</span> ATrousTransform {
    <span class="hljs-comment">// Our output is an image as well as the current level for each</span>
    <span class="hljs-comment">// iteration. The current level is an `Option` to represent the</span>
    <span class="hljs-comment">// final residue layer after the intermediary layers have been</span>
    <span class="hljs-comment">// generated.</span>
    <span class="hljs-class"><span class="hljs-keyword">type</span> <span class="hljs-title">Item</span></span> = (Array2::&lt;<span class="hljs-built_in">f32</span>&gt;, <span class="hljs-built_in">Option</span>&lt;<span class="hljs-built_in">usize</span>&gt;);

    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">next</span></span>(&amp;<span class="hljs-keyword">mut</span> <span class="hljs-keyword">self</span>) -&gt; <span class="hljs-built_in">Option</span>&lt;Self::Item&gt; {
        <span class="hljs-keyword">let</span> pixel_scale = <span class="hljs-keyword">self</span>.current_level;
        <span class="hljs-keyword">self</span>.current_level += <span class="hljs-number">1</span>;

        <span class="hljs-comment">// We've already generated all the layers. Return None to </span>
        <span class="hljs-comment">// exit the iterator.</span>
        <span class="hljs-keyword">if</span> pixel_scale &gt; <span class="hljs-keyword">self</span>.levels {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">None</span>;
        }

        <span class="hljs-comment">// We've generated all intermediary layers, return the </span>
        <span class="hljs-comment">// residue layer.</span>
        <span class="hljs-keyword">if</span> pixel_scale == <span class="hljs-keyword">self</span>.levels {
            <span class="hljs-keyword">return</span> <span class="hljs-literal">Some</span>((<span class="hljs-keyword">self</span>.input.clone(), <span class="hljs-literal">None</span>))
        }

        <span class="hljs-keyword">let</span> (width, height) = (<span class="hljs-keyword">self</span>.width, <span class="hljs-keyword">self</span>.height);

        <span class="hljs-comment">// Distance between adjacent pixels for convolution (also </span>
        <span class="hljs-comment">// referred to as size of "hole").</span>
        <span class="hljs-keyword">let</span> distance = <span class="hljs-number">2_usize</span>.pow(pixel_scale <span class="hljs-keyword">as</span> <span class="hljs-built_in">u32</span>);

        <span class="hljs-comment">// Create new buffer to hold the computed data for this layer.</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> current_data = Array2::&lt;<span class="hljs-built_in">f32</span>&gt;::zeros((height, width));

        <span class="hljs-comment">// Iterate over each pixel location in the 2D image</span>
        <span class="hljs-keyword">for</span> x <span class="hljs-keyword">in</span> <span class="hljs-number">0</span>..width {
            <span class="hljs-keyword">for</span> y <span class="hljs-keyword">in</span> <span class="hljs-number">0</span>..height {
                <span class="hljs-comment">// Set the current pixel in current layer to</span>
                <span class="hljs-comment">// the result of convolution on the current</span>
                <span class="hljs-comment">// pixel in input data.</span>
                current_data[[y, x]] = <span class="hljs-keyword">self</span>.compute_convoluted_pixel(
                    distance, 
                    [x, y]
                );
            }
        }

        <span class="hljs-comment">// Create current layer by subtracting currently computed pixels </span>
        <span class="hljs-comment">// from previous layer</span>
        <span class="hljs-keyword">let</span> final_data = <span class="hljs-keyword">self</span>.input.clone() - &amp;current_data;

        <span class="hljs-comment">// Set the input layer to equal the current computed layer so </span>
        <span class="hljs-comment">// that it can be used as the "previous layer" in next iteration.</span>
        <span class="hljs-comment">// This is also our residue data for each layer.</span>
        <span class="hljs-keyword">self</span>.input = current_data;

        <span class="hljs-comment">// Return the current layer data as well as current level information.</span>
        <span class="hljs-literal">Some</span>((final_data, <span class="hljs-literal">Some</span>(<span class="hljs-keyword">self</span>.current_level)))
    }
}
</code></pre>
<p>I'm going to point out that there's a lot of potential for optimizing for performance here, but that's out of the scope of this article.</p>
<p>We'll finally look at how we can take all of these layers and reconstruct our input image.</p>
<h3 id="heading-recomposition">Recomposition</h3>
<p>As I've said previously, reconstructing an image that was decomposed with the A Trous transform is as simple as summing all of the layers together.</p>
<p>We're going to define a trait for this. Why we need a trait here should be clear once you look at the implementation.</p>
<p>Create a new file <code>recompose.rs</code> with the following contents:</p>
<pre><code class="lang-rust"><span class="hljs-comment">// recompose.rs</span>

<span class="hljs-keyword">use</span> image::{DynamicImage, ImageBuffer, Luma};
<span class="hljs-keyword">use</span> ndarray::Array2;

<span class="hljs-keyword">pub</span> <span class="hljs-class"><span class="hljs-keyword">trait</span> <span class="hljs-title">RecomposableLayers</span></span>: <span class="hljs-built_in">Iterator</span>&lt;Item = (Array2&lt;<span class="hljs-built_in">f32</span>&gt;, <span class="hljs-built_in">Option</span>&lt;<span class="hljs-built_in">usize</span>&gt;)&gt; {
    <span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">recompose_into_image</span></span>(
        <span class="hljs-keyword">self</span>,
        width: <span class="hljs-built_in">usize</span>,
        height: <span class="hljs-built_in">usize</span>,
    ) -&gt; DynamicImage
        <span class="hljs-keyword">where</span>
            <span class="hljs-keyword">Self</span>: <span class="hljs-built_in">Sized</span>,
    {
        <span class="hljs-comment">// Create a result buffer to hold the pixel data for our output image.</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> result = Array2::&lt;<span class="hljs-built_in">f32</span>&gt;::zeros((height, width));

        <span class="hljs-comment">// For each layer, add the layer data to current value of result buffer.</span>
        <span class="hljs-keyword">for</span> layer <span class="hljs-keyword">in</span> <span class="hljs-keyword">self</span> {
            result += &amp;layer.<span class="hljs-number">0</span>;
        }

        <span class="hljs-comment">// Compute min and max pixel intensity values in the final data so that</span>
        <span class="hljs-comment">// we can perform a "rescale", which normalizes all pixel values to be</span>
        <span class="hljs-comment">// between the range of 0 &amp; 1, as is expected by float 32 images.</span>
        <span class="hljs-keyword">let</span> min_pixel = result.iter().copied().reduce(<span class="hljs-built_in">f32</span>::min).unwrap();
        <span class="hljs-keyword">let</span> max_pixel = result.iter().copied().reduce(<span class="hljs-built_in">f32</span>::max).unwrap();

        <span class="hljs-comment">// Create a new `ImageBuffer`, which is a type provided by `image` crate to</span>
        <span class="hljs-comment">// serve as buffer for pixel data of an image. Here, we're creating a new</span>
        <span class="hljs-comment">// `Luma` ImageBuffer with pixel value of type `u16`. Luma just refers to</span>
        <span class="hljs-comment">// grayscale.</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> result_img: ImageBuffer&lt;Luma&lt;<span class="hljs-built_in">u16</span>&gt;, <span class="hljs-built_in">Vec</span>&lt;<span class="hljs-built_in">u16</span>&gt;&gt; =
            ImageBuffer::new(width <span class="hljs-keyword">as</span> <span class="hljs-built_in">u32</span>, height <span class="hljs-keyword">as</span> <span class="hljs-built_in">u32</span>);

        <span class="hljs-comment">// Pre-compute the denominator for scaling computation so that we don't</span>
        <span class="hljs-comment">// repeat this unnecessarily for every iteration.</span>
        <span class="hljs-keyword">let</span> rescale_ratio = max_pixel - min_pixel;

        <span class="hljs-comment">// Iterate over all pixels in the `ImageBuffer` and fill it based on data</span>
        <span class="hljs-comment">// from the `result` buffer after rescaling the value.</span>
        <span class="hljs-keyword">for</span> (x, y, pixel) <span class="hljs-keyword">in</span> result_img.enumerate_pixels_mut() {
            <span class="hljs-keyword">let</span> intensity = result[(y <span class="hljs-keyword">as</span> <span class="hljs-built_in">usize</span>, x <span class="hljs-keyword">as</span> <span class="hljs-built_in">usize</span>)];

            *pixel =
                Luma([((intensity - min_pixel) / rescale_ratio * <span class="hljs-built_in">u16</span>::MAX <span class="hljs-keyword">as</span> <span class="hljs-built_in">f32</span>) <span class="hljs-keyword">as</span> <span class="hljs-built_in">u16</span>]);
        }

        <span class="hljs-comment">// Convert the `ImageBuffer` into `DynamicImage` and return it</span>
        DynamicImage::ImageLuma16(result_img)
    }
}

<span class="hljs-comment">// Implement this trait for anything that implements the Iterator trait</span>
<span class="hljs-comment">// with the given item type</span>
<span class="hljs-keyword">impl</span>&lt;T&gt; RecomposableLayers <span class="hljs-keyword">for</span> T <span class="hljs-keyword">where</span> T: <span class="hljs-built_in">Iterator</span>&lt;Item = (Array2&lt;<span class="hljs-built_in">f32</span>&gt;, <span class="hljs-built_in">Option</span>&lt;<span class="hljs-built_in">usize</span>&gt;)&gt; {}
</code></pre>
<p>If you haven't noticed, since we implement this trait for a generic, this will work with any iterator, such as <code>Filter</code>, <code>Map</code>, and so on. If you didn't use a trait here, you'll have had to implement the same thing again and again for every built-in iterator type, and your code wouldn't have worked with 3rd party types.</p>
<h3 id="heading-using-the-a-trous-transform">Using the <em>À Trous</em> Transform</h3>
<p>After all of that, it's finally time to reproduce the processing that I showed you for the galaxy image with lots of noise. Create a new file <code>main.rs</code> with the following contents:</p>
<pre><code class="lang-rust"><span class="hljs-keyword">use</span> image::{DynamicImage, ImageBuffer, Luma};
<span class="hljs-keyword">use</span> atrous::recompose::RecomposableLayers;
<span class="hljs-keyword">use</span> atrous::transform::ATrousTransform;

<span class="hljs-function"><span class="hljs-keyword">fn</span> <span class="hljs-title">main</span></span>() {
    <span class="hljs-comment">// Open our noisy image</span>
    <span class="hljs-keyword">let</span> image = image::open(<span class="hljs-string">"m33-noise-lum.jpg"</span>).unwrap();

    <span class="hljs-comment">// Create a new instance of the transform with 9 layers</span>
    <span class="hljs-keyword">let</span> transform = ATrousTransform::new(&amp;image, <span class="hljs-number">9</span>);

    <span class="hljs-comment">// Map over each layer</span>
    transform.map(|(<span class="hljs-keyword">mut</span> buffer, pixel_scale)| {
        <span class="hljs-comment">// Create a new image buffer to hold the pixel data. This</span>
        <span class="hljs-comment">// will be populated from the raw buffer for this layer.</span>
        <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> new_buffer =
            ImageBuffer::&lt;Luma&lt;<span class="hljs-built_in">u16</span>&gt;, <span class="hljs-built_in">Vec</span>&lt;<span class="hljs-built_in">u16</span>&gt;&gt;::new(buffer.ncols() <span class="hljs-keyword">as</span> <span class="hljs-built_in">u32</span>, buffer.nrows() <span class="hljs-keyword">as</span> <span class="hljs-built_in">u32</span>);

        <span class="hljs-comment">// Iterate over all pixels of the `ImageBuffer` to populate it. We also</span>
        <span class="hljs-comment">// convert from `f32` pixels to `u16` pixels.</span>
        <span class="hljs-keyword">for</span> (x, y, pixel) <span class="hljs-keyword">in</span> new_buffer.enumerate_pixels_mut() {
            *pixel = Luma([(buffer[[y <span class="hljs-keyword">as</span> <span class="hljs-built_in">usize</span>, x <span class="hljs-keyword">as</span> <span class="hljs-built_in">usize</span>]] * <span class="hljs-built_in">u16</span>::MAX <span class="hljs-keyword">as</span> <span class="hljs-built_in">f32</span>) <span class="hljs-keyword">as</span> <span class="hljs-built_in">u16</span>])
        }

        <span class="hljs-comment">// If the present layer is a small scale layer (&lt; 3), </span>
        <span class="hljs-comment">// perform noise reduction</span>
        <span class="hljs-keyword">if</span> pixel_scale.is_some_and(|scale| scale &lt; <span class="hljs-number">3</span>) {
            <span class="hljs-keyword">let</span> <span class="hljs-keyword">mut</span> image = DynamicImage::ImageLuma16(new_buffer).to_luma8();

            <span class="hljs-comment">// Bilateral filter is a de-noising filter. Apply it to the image.</span>
            image = imageproc::filter::bilateral_filter(&amp;image, <span class="hljs-number">10</span>, <span class="hljs-number">10</span>., <span class="hljs-number">3</span>.);

            <span class="hljs-comment">// Modify the raw buffer to contain the updated pixel values after</span>
            <span class="hljs-comment">// filtering.</span>
            <span class="hljs-keyword">for</span> (x, y, pixel) <span class="hljs-keyword">in</span> image.enumerate_pixels() {
                buffer[[y <span class="hljs-keyword">as</span> <span class="hljs-built_in">usize</span>, x <span class="hljs-keyword">as</span> <span class="hljs-built_in">usize</span>]] = pixel.<span class="hljs-number">0</span>[<span class="hljs-number">0</span>] <span class="hljs-keyword">as</span> <span class="hljs-built_in">f32</span> / <span class="hljs-built_in">u8</span>::MAX <span class="hljs-keyword">as</span> <span class="hljs-built_in">f32</span>;
            }

            <span class="hljs-comment">// Return the updated buffer.</span>
            (buffer, pixel_scale)
        } <span class="hljs-keyword">else</span> {
            <span class="hljs-comment">// Return the unmodified buffer for larger scale layers.</span>
            (buffer, pixel_scale)
        }
    })
        <span class="hljs-comment">// Call the recomposition method on iterator</span>
        .recompose_into_image(image.width() <span class="hljs-keyword">as</span> <span class="hljs-built_in">usize</span>, image.height() <span class="hljs-keyword">as</span> <span class="hljs-built_in">usize</span>)
        <span class="hljs-comment">// Convert output to 8-bit grayscale image</span>
        .to_luma8()
        <span class="hljs-comment">// Save it to jpg file</span>
        .save(<span class="hljs-string">"noise-reduced.jpg"</span>)
        .unwrap()
}
</code></pre>
<p>You also need to add a new dependency, <code>imageproc</code>, which provides useful image processing implementations on top of the <code>image</code> crate.</p>
<pre><code class="lang-shell">cargo add imageproc
</code></pre>
<p>To make this work, we also need to modify our <code>Cargo.toml</code> to explicitly define both binary and library targets:</p>
<pre><code class="lang-toml">// Cargo.toml

<span class="hljs-section">[package]</span>
<span class="hljs-attr">name</span> = <span class="hljs-string">"atrous-rs"</span>
<span class="hljs-attr">version</span> = <span class="hljs-string">"0.1.0"</span>
<span class="hljs-attr">edition</span> = <span class="hljs-string">"2021"</span>

<span class="hljs-section">[[bin]]</span>
<span class="hljs-attr">name</span> = <span class="hljs-string">"atrous"</span>
<span class="hljs-attr">path</span> = <span class="hljs-string">"src/main.rs"</span>

<span class="hljs-section">[lib]</span>
<span class="hljs-attr">name</span> = <span class="hljs-string">"atrous"</span>
<span class="hljs-attr">path</span> = <span class="hljs-string">"src/lib.rs"</span>

<span class="hljs-comment"># See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html</span>

<span class="hljs-section">[dependencies]</span>
<span class="hljs-attr">image</span> = <span class="hljs-string">"0.25.1"</span>
<span class="hljs-attr">imageproc</span> = <span class="hljs-string">"0.24.0"</span>
<span class="hljs-attr">ndarray</span> = <span class="hljs-string">"0.15.6"</span>
</code></pre>
<p>You may download the test image from <a target="_blank" href="https://anshulsanghi-assets.s3.ap-south-1.amazonaws.com/m33-noise-lum.jpg">here</a>. Move it to the root directory of your project, and run <code>cargo run --release</code>. Once it finishes, you should have a new file <code>noise-reduced.jpg</code> as the output of our process.</p>
<p>And there we have it.</p>
<h2 id="heading-further-reading">Further Reading</h2>
<p>These are some of the resources that were very helpful to me when I was learning about this algorithm and how to use it. I highly encourage anyone who wants a more technical understanding of the algorithm to check these out.</p>
<ul>
<li><a target="_blank" href="https://www.eso.org/sci/software/esomidas/doc/user/18NOV/volb/node317.html">The <em>à trous</em> algorithm</a> </li>
<li><a target="_blank" href="https://www.pixinsight.com/doc/legacy/LE/20_wavelets/a_trous_wavelet_transform/a_trous_wavelet_transform.html">The <em>À Trous</em> Discrete Wavelet Transform In PixInsight</a></li>
<li><a target="_blank" href="https://jstarck.cosmostat.org/publications/books/book2/">Astronomical Image and Data Analysis</a> by Jean-Luc Starck and Fionn Murtagh</li>
<li><a target="_blank" href="https://jstarck.cosmostat.org/publications/books/book-2015/">Sparse Image and Signal Processing: Wavelets and Related Geometric Multiscale Analysis</a> by J.L. Starck, F. Murtagh, and J. Fadili</li>
</ul>
<p>In addition, I've created a Rust library for working with <em>À Trous</em> transform. It closely matches with what I've showed you here, but has some additional features already, and will have even more. </p>
<p>Things such as handling RGB images and working with all the 3 different kernels is already implemented. It also has better logic for handling boundary conditions, where it uses the image folding technique.</p>
<p>I'm also soon going to be working on performance improvements for the same.</p>
<p>If you want to learn more, or contribute to the library, feel free to do so. The repository can be found here: <a target="_blank" href="https://github.com/anshap1719/image-dwt">https://github.com/anshap1719/image-dwt</a>  </p>
<h2 id="heading-wrapping-up">Wrapping Up</h2>
<p>I hope you enjoyed the journey so far. If image processing techniques or their implementation in Rust is something that interests you, then stay tuned for more as these are the topics I love writing about.</p>
<p>Also, feel free to <a target="_blank" href="mailto:nitric-brisk.0s@icloud.com"><strong>contact me</strong></a> if you have any questions or opinions on this topic.</p>
<h3 id="heading-enjoying-my-work">Enjoying my work?</h3>
<p>Consider buying me a coffee to support my work!</p>


<p>Till next time, happy coding and wishing you clear skies!</p>
 ]]>
                </content:encoded>
            </item>
        
    </channel>
</rss>
