<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: icsboyx</title>
    <description>The latest articles on DEV Community by icsboyx (@icsboyx).</description>
    <link>https://dev.to/icsboyx</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3906362%2Fe05a1b5f-a735-42f5-afad-5d9bd79a80af.png</url>
      <title>DEV Community: icsboyx</title>
      <link>https://dev.to/icsboyx</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/icsboyx"/>
    <language>en</language>
    <item>
      <title>[Boost]</title>
      <dc:creator>icsboyx</dc:creator>
      <pubDate>Wed, 20 May 2026 11:39:22 +0000</pubDate>
      <link>https://dev.to/icsboyx/-28nd</link>
      <guid>https://dev.to/icsboyx/-28nd</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/icsboyx/i-just-wanted-to-reuse-async-functions-in-rust-i-ended-up-building-a-tiny-task-manager-5fll" class="crayons-story__hidden-navigation-link"&gt;I Just Wanted to Reuse Async Functions in Rust. I Ended Up Building a Tiny Task Manager&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/icsboyx" class="crayons-avatar  crayons-avatar--l  "&gt;
            &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3906362%2Fe05a1b5f-a735-42f5-afad-5d9bd79a80af.png" alt="icsboyx profile" class="crayons-avatar__image" width="260" height="260"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/icsboyx" class="crayons-story__secondary fw-medium m:hidden"&gt;
              icsboyx
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                icsboyx
                
              
              &lt;div id="story-author-preview-content-3709011" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/icsboyx" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&gt;
                        &lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3906362%2Fe05a1b5f-a735-42f5-afad-5d9bd79a80af.png" class="crayons-avatar__image" alt="" width="260" height="260"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;icsboyx&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/icsboyx/i-just-wanted-to-reuse-async-functions-in-rust-i-ended-up-building-a-tiny-task-manager-5fll" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;May 20&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/icsboyx/i-just-wanted-to-reuse-async-functions-in-rust-i-ended-up-building-a-tiny-task-manager-5fll" id="article-link-3709011"&gt;
          I Just Wanted to Reuse Async Functions in Rust. I Ended Up Building a Tiny Task Manager
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/rust"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;rust&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tokio"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tokio&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/async"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;async&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/beginners"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;beginners&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
            &lt;a href="https://dev.to/icsboyx/i-just-wanted-to-reuse-async-functions-in-rust-i-ended-up-building-a-tiny-task-manager-5fll#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            11 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
    </item>
    <item>
      <title>I Just Wanted to Reuse Async Functions in Rust. I Ended Up Building a Tiny Task Manager</title>
      <dc:creator>icsboyx</dc:creator>
      <pubDate>Wed, 20 May 2026 11:38:47 +0000</pubDate>
      <link>https://dev.to/icsboyx/i-just-wanted-to-reuse-async-functions-in-rust-i-ended-up-building-a-tiny-task-manager-5fll</link>
      <guid>https://dev.to/icsboyx/i-just-wanted-to-reuse-async-functions-in-rust-i-ended-up-building-a-tiny-task-manager-5fll</guid>
      <description>&lt;h1&gt;
  
  
  I Just Wanted to Reuse Async Functions in Rust. I Ended Up Building a Tiny Task Manager
&lt;/h1&gt;

&lt;p&gt;I am currently studying Rust, and one of the things that kept bothering me was surprisingly simple:&lt;/p&gt;

&lt;p&gt;How do I reuse an async function more than once in a clean way?&lt;/p&gt;

&lt;p&gt;At first, this did not look like a big problem. I already had async functions. I already had Tokio. I could already spawn tasks.&lt;/p&gt;

&lt;p&gt;So what was the issue?&lt;/p&gt;

&lt;p&gt;The issue was that I did not just want to run a task once.&lt;/p&gt;

&lt;p&gt;I wanted to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;register different async functions&lt;/li&gt;
&lt;li&gt;run them inside the same async system&lt;/li&gt;
&lt;li&gt;treat them as generic tasks&lt;/li&gt;
&lt;li&gt;restart some of them depending on their role&lt;/li&gt;
&lt;li&gt;keep the system logic separate from the task logic&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the point where "I just need an async function" slowly becomes "I think I am building a task manager".&lt;/p&gt;

&lt;p&gt;This article is the story of that shift.&lt;/p&gt;

&lt;p&gt;If you want to follow along with the code, the project is here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/icsboyx/async_task_manager" rel="noopener noreferrer"&gt;https://github.com/icsboyx/async_task_manager&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And this is the central idea that finally made the whole thing click for me:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A &lt;code&gt;Future&lt;/code&gt; is not a task definition.&lt;br&gt;&lt;br&gt;
A &lt;code&gt;Future&lt;/code&gt; is one execution.&lt;br&gt;&lt;br&gt;
If I want restartable async work, I should store the recipe: &lt;code&gt;Fn() -&amp;gt; Future&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The Real Problem Was Not Async
&lt;/h2&gt;

&lt;p&gt;The real problem was not writing async functions.&lt;/p&gt;

&lt;p&gt;Rust makes it easy to write something like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;task1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;anyhow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;loop&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"task 1 is running"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nn"&gt;tokio&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;time&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Duration&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_secs&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This function intentionally never returns, so there is no trailing &lt;code&gt;Ok(())&lt;/code&gt;.&lt;br&gt;
If I later add shutdown logic, I can exit the loop cleanly with something like &lt;code&gt;break Ok(())&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The problem starts when you want to say:&lt;/p&gt;

&lt;p&gt;"I want to store multiple async functions, possibly with different behaviors, and start them when I want."&lt;/p&gt;

&lt;p&gt;That sounds natural, but in Rust it immediately forces you to think about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;concrete types&lt;/li&gt;
&lt;li&gt;futures&lt;/li&gt;
&lt;li&gt;trait objects&lt;/li&gt;
&lt;li&gt;closures&lt;/li&gt;
&lt;li&gt;pinning&lt;/li&gt;
&lt;li&gt;ownership&lt;/li&gt;
&lt;li&gt;reusability&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And that is where I hit my first wall.&lt;/p&gt;
&lt;h2&gt;
  
  
  My First Wrong Mental Model
&lt;/h2&gt;

&lt;p&gt;My first instinct was something like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;task1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task3&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Conceptually, that is exactly what I wanted:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;here are my tasks&lt;/li&gt;
&lt;li&gt;keep them in a collection&lt;/li&gt;
&lt;li&gt;run them when needed&lt;/li&gt;
&lt;li&gt;restart them if needed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But that nice mental model hides the real issue.&lt;/p&gt;

&lt;p&gt;Each &lt;code&gt;async fn&lt;/code&gt; returns a future, and each future has its own concrete type.&lt;br&gt;
So the moment I want one task manager to store several different async starters behind one uniform interface, I need to be much more explicit about the abstraction I want.&lt;/p&gt;

&lt;p&gt;That was the first real lesson:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If I want to restart async work, I should not store a future that is already created.&lt;br&gt;&lt;br&gt;
I should store something that can create a fresh future every time.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That single realization changed the whole design.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;code&gt;task1&lt;/code&gt; vs &lt;code&gt;task1()&lt;/code&gt;: The Difference That Actually Matters
&lt;/h2&gt;

&lt;p&gt;This was the point where Rust forced me to think more clearly.&lt;/p&gt;

&lt;p&gt;The first mistake would be trying to store this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nf"&gt;task1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Why is that wrong for my use case?&lt;/p&gt;

&lt;p&gt;Because &lt;code&gt;task1()&lt;/code&gt; is not the task definition.&lt;br&gt;
It is a &lt;strong&gt;future instance&lt;/strong&gt; created right now.&lt;/p&gt;

&lt;p&gt;That means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;it represents one execution&lt;/li&gt;
&lt;li&gt;once that execution finishes, that value is done&lt;/li&gt;
&lt;li&gt;if I want to restart the task, I do not want the old future back&lt;/li&gt;
&lt;li&gt;I need a brand new future for the next run&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So the real thing I needed was not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nf"&gt;task1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="n"&gt;task1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;or, more generally:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nf"&gt;Fn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is the key mental shift.&lt;/p&gt;

&lt;h2&gt;
  
  
  Function Item, Function Pointer, Closure, &lt;code&gt;dyn Fn&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This also helped me understand something I had been hand-waving before.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;task1&lt;/code&gt; is not automatically "a dynamic function".&lt;/p&gt;

&lt;p&gt;At first, it is a &lt;strong&gt;function item&lt;/strong&gt;: a concrete value tied to one exact function.&lt;br&gt;
Rust knows exactly which function it is and gives it a concrete type.&lt;/p&gt;

&lt;p&gt;From there, I can move through more general callable shapes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;task1&lt;/code&gt;: a concrete function item&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;fn() -&amp;gt; _&lt;/code&gt;: a function pointer&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;move || ...&lt;/code&gt;: a closure&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dyn Fn() -&amp;gt; _&lt;/code&gt;: a dynamically dispatched callable trait object&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That distinction mattered a lot for me, because the real transition was not just:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;from &lt;code&gt;task1()&lt;/code&gt; to &lt;code&gt;task1&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;but:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;from one concrete callable&lt;/li&gt;
&lt;li&gt;toward one uniform callable abstraction I could store in the task manager&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once I saw it that way, the problem stopped being "why is Rust being difficult?" and became "what callable shape do I actually need?"&lt;/p&gt;
&lt;h2&gt;
  
  
  The Important Type Transition: From Concrete to Dynamic
&lt;/h2&gt;

&lt;p&gt;This was probably the most important Rust lesson in the whole project.&lt;/p&gt;

&lt;p&gt;When I write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;task1&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;anyhow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rust gives me a concrete future type behind the scenes.&lt;br&gt;
The same is true for &lt;code&gt;task2&lt;/code&gt;, &lt;code&gt;task3&lt;/code&gt;, and every other &lt;code&gt;async fn&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;So even if all my functions look conceptually similar, Rust still sees different concrete future types.&lt;/p&gt;

&lt;p&gt;That means this line is doing two jobs in the article:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;task1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;task3&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Repetita iuvant.&lt;/strong&gt;&lt;br&gt;
Yes, I am showing the same line again on purpose.&lt;br&gt;
The first time, it was my naive mental model.&lt;br&gt;
This time, it marks the exact point where the type system pushes back.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If I want a &lt;code&gt;HashMap&lt;/code&gt;, a queue, or a task manager to store all tasks uniformly, I need one shared stored type.&lt;br&gt;
I cannot keep each future in its own concrete shape and still expect one collection to treat them all the same.&lt;/p&gt;

&lt;p&gt;So this is where I had to make the move that, for me, changed the whole design:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;from one concrete future type&lt;/li&gt;
&lt;li&gt;to one dynamic future abstraction I could actually store&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Because this is the point where Rust stops feeling like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I wrote some async functions&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and starts feeling like:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;I need to model a reusable async execution boundary&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In other words, I stopped thinking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Store this exact future type.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;and started thinking:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Store anything that behaves like a future returning &lt;code&gt;Result&amp;lt;()&amp;gt;&lt;/code&gt;, even if the concrete future behind it changes from task to task.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In cleaned-up form, the idea that finally made sense to me was this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;
    &lt;span class="nn"&gt;future&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nn"&gt;pin&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Pin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nn"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;TaskFuture&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nb"&gt;Pin&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nn"&gt;anyhow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;'static&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;TaskFn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nb"&gt;Arc&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nf"&gt;Fn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;TaskFuture&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;'static&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And this was exactly the moment where my brain went:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;wow wow wow wow wow... what the duck is this?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Because the first time you see something like this while learning Rust, it does not look like "a reusable task".&lt;br&gt;
It looks like somebody dropped an entire type system on your keyboard.&lt;/p&gt;

&lt;p&gt;And yet, once I slowed down and read it piece by piece, it stopped looking absurd.&lt;/p&gt;

&lt;p&gt;This pair of types says exactly what I need:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a callable thing I can keep&lt;/li&gt;
&lt;li&gt;that I can invoke more than once&lt;/li&gt;
&lt;li&gt;and that gives me an owned future I can spawn each time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the real abstraction boundary in the design.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why &lt;code&gt;dyn Future&lt;/code&gt; Needs &lt;code&gt;Box&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Trait objects like &lt;code&gt;dyn Future&lt;/code&gt; do not have a known size at compile time.&lt;/p&gt;

&lt;p&gt;Rust therefore cannot store them directly by value.&lt;br&gt;
I need indirection.&lt;/p&gt;

&lt;p&gt;That is why the owned dynamic future shape is typically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nn"&gt;anyhow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Box&lt;/code&gt; gives me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;one stable heap allocation for the concrete future&lt;/li&gt;
&lt;li&gt;one uniform, sized handle I can store&lt;/li&gt;
&lt;li&gt;a way to erase the concrete future type behind a common interface&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The same idea applies to the callable itself.&lt;/p&gt;

&lt;p&gt;I do not want one &lt;code&gt;Task&amp;lt;F, Fut&amp;gt;&lt;/code&gt; per task starter.&lt;br&gt;
I want one storable task function type shared by the whole manager.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why &lt;code&gt;Pin&amp;lt;Box&amp;lt;dyn Future&amp;lt;...&amp;gt;&amp;gt;&amp;gt;&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This was another thing that looked scary until it clicked.&lt;/p&gt;

&lt;p&gt;Many futures produced by &lt;code&gt;async fn&lt;/code&gt; are not &lt;code&gt;Unpin&lt;/code&gt;.&lt;br&gt;
After polling starts, Rust may require that they are not moved around in memory.&lt;/p&gt;

&lt;p&gt;That is why the common owned dynamic future shape is not just:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nn"&gt;anyhow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;but:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nb"&gt;Pin&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nn"&gt;anyhow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;dyn Future&lt;/code&gt; gives me dynamic dispatch&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Box&lt;/code&gt; gives me indirection and a sized owned container&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Pin&lt;/code&gt; gives me the movement guarantees a future may require&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Once that clicked, the ugly type stopped looking random.&lt;br&gt;
It started looking like a precise description of the thing I actually needed to own.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Closure That Makes the Whole Thing Reusable
&lt;/h2&gt;

&lt;p&gt;Even after understanding the dynamic future type, I still needed a bridge between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the original concrete callable&lt;/li&gt;
&lt;li&gt;and the uniform task function type I wanted to store&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That bridge is the closure.&lt;/p&gt;

&lt;p&gt;In cleaned-up form, the constructor looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;new&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Fut&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="nb"&gt;Into&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;task_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TaskType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="n"&gt;F&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;Fn&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Fut&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;'static&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;Fut&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Future&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nn"&gt;anyhow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;'static&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;start_fn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TaskFn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Arc&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;||&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;Box&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;pin&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;function&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;TaskFuture&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Uuid&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_v4&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;start_fn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;task_type&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At first glance, this looks like glue code.&lt;/p&gt;

&lt;p&gt;It is not.&lt;/p&gt;

&lt;p&gt;This closure is doing the real adaptation work.&lt;/p&gt;

&lt;p&gt;It does three jobs at once:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;it &lt;strong&gt;owns the original concrete callable&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;it &lt;strong&gt;calls it each time a fresh execution is needed&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;it &lt;strong&gt;erases the concrete future type into &lt;code&gt;TaskFuture&lt;/code&gt;&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is what makes the design restartable.&lt;/p&gt;

&lt;p&gt;Without this adapter, I would not really have a reusable task definition.&lt;br&gt;
I would just have a bunch of specific functions with no common storage shape.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;move&lt;/code&gt; also matters here.&lt;br&gt;
It makes the closure take ownership of &lt;code&gt;function&lt;/code&gt;, so the task can keep that callable around and use it later, long after &lt;code&gt;Task::new&lt;/code&gt; has returned.&lt;/p&gt;

&lt;p&gt;If I had to summarize this entire section in one sentence, it would be:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The reusable thing is not the future.&lt;br&gt;&lt;br&gt;
The reusable thing is the factory that creates a fresh future on demand.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h2&gt;
  
  
  A Diagram of What Actually Happens
&lt;/h2&gt;

&lt;p&gt;Here is the transformation that finally helped me think about it clearly:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F94nn78zqf3kdlvil1fov.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F94nn78zqf3kdlvil1fov.png" alt="Async task flow: from async function to Tokio JoinSet" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For reference, this is the Mermaid source:&lt;br&gt;

  Show the Mermaid source
  &lt;br&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;flowchart LR
    A["Concrete async function&amp;lt;br/&amp;gt;task1"] --&amp;gt; B["Concrete callable&amp;lt;br/&amp;gt;F: Fn() -&amp;gt; Fut"]
    B --&amp;gt; C["Adapter closure&amp;lt;br/&amp;gt;move || Box::pin(function())"]
    C --&amp;gt; D["Dynamic task factory&amp;lt;br/&amp;gt;TaskFn"]
    D --&amp;gt; E["Stored inside Task&amp;lt;br/&amp;gt;start_fn"]
    E --&amp;gt; F["Called by TaskManager"]
    F --&amp;gt; G["Fresh owned future&amp;lt;br/&amp;gt;TaskFuture"]
    G --&amp;gt; H["Spawned into Tokio JoinSet"]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;




&lt;/p&gt;

&lt;p&gt;This is also the point where the whole thing started to feel like function inception:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;a function that stores a function that creates a future that runs a task&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ridiculous? A little.&lt;br&gt;
Correct? Also yes.&lt;/p&gt;
&lt;h2&gt;
  
  
  Building a Task Type
&lt;/h2&gt;

&lt;p&gt;Once I understood that I needed a restartable task factory, the next step was creating a task representation with both execution behavior and system meaning.&lt;/p&gt;

&lt;p&gt;Conceptually, the task shape became:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Task&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;uuid&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;start_fn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TaskFn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;task_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;TaskType&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I liked this design because each task now had:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an identity&lt;/li&gt;
&lt;li&gt;a name for logs&lt;/li&gt;
&lt;li&gt;a reusable startup function&lt;/li&gt;
&lt;li&gt;a system-level policy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And that last point turned out to be very important.&lt;/p&gt;

&lt;h2&gt;
  
  
  Running a Task Is Not Enough. You Need to Know What It Means
&lt;/h2&gt;

&lt;p&gt;While building the manager, I realized that "a task ended" is not enough information.&lt;/p&gt;

&lt;p&gt;A task manager should not only know how to run a task.&lt;br&gt;
It should know what that task means to the system.&lt;/p&gt;

&lt;p&gt;Some tasks are optional.&lt;br&gt;
Some tasks should stop the whole system when they end.&lt;br&gt;
Some tasks are essential enough that if they disappear, the rest of the system no longer makes sense.&lt;/p&gt;

&lt;p&gt;That is why I introduced this enum:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;TaskType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;ShutDownOnExit&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nf"&gt;Mandatory&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;Detachable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;i32&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was a big architectural improvement, because now the manager was not just executing tasks.&lt;br&gt;
It was interpreting them.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;ShutDownOnExit&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;If this task exits, the manager stops.&lt;/p&gt;

&lt;p&gt;This is perfect for something like a &lt;code&gt;ctrl_c_handler&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;Mandatory(restarts)&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This task is essential for the system.&lt;/p&gt;

&lt;p&gt;If it exits, restart it while budget remains.&lt;br&gt;
If the restart budget is exhausted, stop the whole manager.&lt;/p&gt;

&lt;p&gt;For me, this means:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;if this task is gone, keeping the rest of the system alive no longer makes sense&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3&gt;
  
  
  &lt;code&gt;Detachable(restarts)&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This task is useful, but not essential.&lt;/p&gt;

&lt;p&gt;If it exits successfully, I can discard it.&lt;br&gt;
If it fails, I can restart it while budget remains.&lt;br&gt;
If the restart budget is exhausted, I discard it and let the rest of the system continue.&lt;/p&gt;

&lt;p&gt;That made the whole project feel much more like a small supervised async system, and much less like a pile of random spawned tasks.&lt;/p&gt;
&lt;h2&gt;
  
  
  The Restart Counter Was More Subtle Than I Expected
&lt;/h2&gt;

&lt;p&gt;Another thing that looked easy at first was restart logic.&lt;/p&gt;

&lt;p&gt;I thought:&lt;/p&gt;

&lt;p&gt;"Fine, I just decrement a counter and restart the task."&lt;/p&gt;

&lt;p&gt;But once I started implementing it, I had to answer a more precise question:&lt;/p&gt;

&lt;p&gt;What does &lt;code&gt;3&lt;/code&gt; actually mean?&lt;/p&gt;

&lt;p&gt;Does it mean:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the task can run 3 times total?&lt;/li&gt;
&lt;li&gt;the task can fail 3 times?&lt;/li&gt;
&lt;li&gt;the task can be restarted 3 times after the first start?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I chose the last meaning.&lt;/p&gt;

&lt;p&gt;So in my design:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Mandatory(3)&lt;/code&gt; or &lt;code&gt;Detachable(3)&lt;/code&gt; means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;1 initial start&lt;/li&gt;
&lt;li&gt;up to 3 restarts&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That sounds simple, but it forced me to be careful with off-by-one logic.&lt;/p&gt;

&lt;p&gt;The behavior I wanted was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;if remaining restarts are greater than zero, restart and decrement&lt;/li&gt;
&lt;li&gt;if remaining restarts are zero, do not restart anymore&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That was one of those small implementation details that taught me a lot about translating a verbal idea into precise behavior.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why &lt;code&gt;JoinSet&lt;/code&gt; Helped a Lot
&lt;/h2&gt;

&lt;p&gt;Once I had reusable task factories, I still needed a way to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;spawn multiple tasks&lt;/li&gt;
&lt;li&gt;wait for whichever one completes next&lt;/li&gt;
&lt;li&gt;know which task completed&lt;/li&gt;
&lt;li&gt;separate task completion from task policy decisions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;code&gt;tokio::task::JoinSet&lt;/code&gt; was a very good fit for this.&lt;/p&gt;

&lt;p&gt;The manager can spawn tasks into the set and then wait for completions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.task_set&lt;/span&gt;&lt;span class="nf"&gt;.join_next_with_id&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// handle task completion here&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;with_id&lt;/code&gt; part matters a lot.&lt;/p&gt;

&lt;p&gt;If a task finishes, I do not just want to know that &lt;em&gt;something&lt;/em&gt; finished.&lt;br&gt;
I want to know &lt;strong&gt;which task&lt;/strong&gt; finished, so I can:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;remove it from the active set&lt;/li&gt;
&lt;li&gt;look up its metadata&lt;/li&gt;
&lt;li&gt;apply the correct restart or shutdown policy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Another subtle but important point is that if the spawned task returns &lt;code&gt;Result&amp;lt;(), anyhow::Error&amp;gt;&lt;/code&gt;, then the join result is nested.&lt;/p&gt;

&lt;p&gt;In practice, that means these cases are different:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(()))))&lt;/span&gt;   &lt;span class="c1"&gt;// Tokio join succeeded and the task returned Ok&lt;/span&gt;
&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;))))&lt;/span&gt;   &lt;span class="c1"&gt;// Tokio join succeeded but the task returned an application error&lt;/span&gt;
&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;             &lt;span class="c1"&gt;// Tokio JoinError: panic, cancellation, abort, etc.&lt;/span&gt;
&lt;span class="nb"&gt;None&lt;/span&gt;                     &lt;span class="c1"&gt;// No more tasks in the JoinSet&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That distinction matters because "the join worked" is not the same thing as "the task succeeded".&lt;/p&gt;

&lt;p&gt;&lt;code&gt;JoinSet&lt;/code&gt; gave me a clean place to separate:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;execution mechanics&lt;/li&gt;
&lt;li&gt;task outcome&lt;/li&gt;
&lt;li&gt;system policy&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And that made the task manager much easier to reason about.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Core Idea of the Manager
&lt;/h2&gt;

&lt;p&gt;At a high level, the flow became:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create tasks and push them into a queue.&lt;/li&gt;
&lt;li&gt;Spawn them into a &lt;code&gt;JoinSet&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Wait for whichever task finishes next.&lt;/li&gt;
&lt;li&gt;Identify the task.&lt;/li&gt;
&lt;li&gt;Inspect how it ended.&lt;/li&gt;
&lt;li&gt;Apply the policy defined by &lt;code&gt;TaskType&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Restart, discard, or stop the system.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;That is the moment where the project stopped being "a few async functions" and became a tiny async supervisor.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Part That Taught Me the Most
&lt;/h2&gt;

&lt;p&gt;If I had to summarize what this project really taught me, it would be this:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Reusability in async Rust is about factories
&lt;/h3&gt;

&lt;p&gt;If I want to execute the same async logic multiple times, I should usually store something that creates a future, not the future itself.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. System behavior should not live inside each task
&lt;/h3&gt;

&lt;p&gt;The task should focus on doing work.&lt;br&gt;
The manager should decide what to do when that work ends.&lt;/p&gt;

&lt;p&gt;That separation made the code much easier to reason about.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Semantics matter more than syntax
&lt;/h3&gt;

&lt;p&gt;A lot of the difficulty was not "how do I write this in Rust?"&lt;/p&gt;

&lt;p&gt;It was:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;what exactly does restart mean?&lt;/li&gt;
&lt;li&gt;when should the whole system stop?&lt;/li&gt;
&lt;li&gt;which tasks are optional and which are essential?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are architecture questions, not syntax questions.&lt;/p&gt;

&lt;p&gt;Rust just forces you to answer them more explicitly.&lt;/p&gt;

&lt;h2&gt;
  
  
  Current Shape of the Project
&lt;/h2&gt;

&lt;p&gt;Right now, the project is small on purpose.&lt;/p&gt;

&lt;p&gt;It starts a few demo tasks, including a &lt;code&gt;Ctrl+C&lt;/code&gt; handler, and routes their behavior through a small task manager with different policies.&lt;/p&gt;

&lt;p&gt;It is not a production-grade supervisor, and I do not think it needs to be.&lt;/p&gt;

&lt;p&gt;For me, it is a learning project with a very specific goal:&lt;/p&gt;

&lt;p&gt;to understand how to reuse async functions cleanly, and how to build a small but structured execution model around them.&lt;/p&gt;

&lt;p&gt;And honestly, I think that is why this project was useful.&lt;/p&gt;

&lt;p&gt;It did not try to solve everything.&lt;br&gt;
It solved one problem that kept bothering me, and in doing so it forced me to understand several Rust concepts more deeply.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Thought
&lt;/h2&gt;

&lt;p&gt;I started with a very small question:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;How can I reuse async functions in Rust without turning everything into a mess?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I ended up learning about:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;boxed futures&lt;/li&gt;
&lt;li&gt;trait objects&lt;/li&gt;
&lt;li&gt;closures as adapters&lt;/li&gt;
&lt;li&gt;pinning&lt;/li&gt;
&lt;li&gt;task supervision&lt;/li&gt;
&lt;li&gt;restart policy design&lt;/li&gt;
&lt;li&gt;async orchestration with Tokio&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is one of the things I like most about learning Rust:&lt;/p&gt;

&lt;p&gt;you often start by trying to solve a local problem, and end up improving the way you think about the whole system.&lt;/p&gt;

&lt;p&gt;If you are learning Rust too, and you have hit the same wall around reusable async functions, I hope this helps.&lt;/p&gt;

&lt;p&gt;Sometimes the real progress is not just making the code compile.&lt;br&gt;
Sometimes it is finally understanding what kind of abstraction you actually needed.&lt;/p&gt;

&lt;p&gt;If you want to explore the code itself, you can find the project here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/icsboyx/async_task_manager" rel="noopener noreferrer"&gt;https://github.com/icsboyx/async_task_manager&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>tokio</category>
      <category>async</category>
      <category>beginners</category>
    </item>
    <item>
      <title>My Tiny Rust Utils, Part 5: mod.rs</title>
      <dc:creator>icsboyx</dc:creator>
      <pubDate>Thu, 30 Apr 2026 16:13:25 +0000</pubDate>
      <link>https://dev.to/icsboyx/my-tiny-rust-utils-part-5-modrs-3nkm</link>
      <guid>https://dev.to/icsboyx/my-tiny-rust-utils-part-5-modrs-3nkm</guid>
      <description>&lt;p&gt;This last part is about the least impressive file in the crate:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;helpers&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;macros&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;save_load&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;mod&lt;/span&gt; &lt;span class="n"&gt;web&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That is &lt;code&gt;src/utils/mod.rs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It is just four lines.&lt;/p&gt;

&lt;p&gt;And maybe that is why it feels like the right place to end the series.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why a boring file still matters
&lt;/h2&gt;

&lt;p&gt;I like this file because it does not pretend to be smarter than it is.&lt;/p&gt;

&lt;p&gt;It just says:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;these are the utility modules&lt;/li&gt;
&lt;li&gt;they live together&lt;/li&gt;
&lt;li&gt;this is the shape of the toolbox&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After the previous parts, that shape is easier to read:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;macros.rs&lt;/code&gt; helps me see what the program is doing&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;helpers.rs&lt;/code&gt; removes a bit of error and JSON friction&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;save_load.rs&lt;/code&gt; gives me one place for config-like persistence&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;web.rs&lt;/code&gt; keeps HTTP glue from leaking everywhere&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;None of those files is huge.&lt;/p&gt;

&lt;p&gt;None of them is a framework.&lt;/p&gt;

&lt;p&gt;That is very intentional.&lt;/p&gt;

&lt;p&gt;I think I work better when the code is allowed to stay small and honest for as long as possible.&lt;/p&gt;

&lt;h2&gt;
  
  
  How the crate feels to me
&lt;/h2&gt;

&lt;p&gt;This &lt;code&gt;utils&lt;/code&gt; crate is not something I built because I wanted to be abstract.&lt;/p&gt;

&lt;p&gt;It is something I built because I know how I work.&lt;/p&gt;

&lt;p&gt;If I do not create a little structure around repeated pain points, I end up with the same code copied in slightly different ways across the project.&lt;/p&gt;

&lt;p&gt;And then I get annoyed with myself later.&lt;/p&gt;

&lt;p&gt;So this crate is a compromise between two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;I do not want ceremony&lt;/li&gt;
&lt;li&gt;I also do not want chaos&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is really the whole story.&lt;/p&gt;

&lt;h2&gt;
  
  
  A more personal ending
&lt;/h2&gt;

&lt;p&gt;I think this is the part where I should be honest.&lt;/p&gt;

&lt;p&gt;For me, coding is not really "work" in the emotional sense, even when it is tiring.&lt;/p&gt;

&lt;p&gt;It is more like a mix of:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;joy&lt;/li&gt;
&lt;li&gt;frustration&lt;/li&gt;
&lt;li&gt;curiosity&lt;/li&gt;
&lt;li&gt;stubbornness&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sometimes it is fun.&lt;/p&gt;

&lt;p&gt;Sometimes it is painful in a very specific and ridiculous way.&lt;/p&gt;

&lt;p&gt;Sometimes a tiny bug ruins half a day.&lt;/p&gt;

&lt;p&gt;Sometimes a four-line helper makes the whole project feel lighter again.&lt;/p&gt;

&lt;p&gt;That is why I keep doing it.&lt;/p&gt;

&lt;p&gt;Not because every line of code is important.&lt;/p&gt;

&lt;p&gt;Not because every project needs to become a product.&lt;/p&gt;

&lt;p&gt;But because coding, for me, is still one of the nicest forms of mental exercise I know.&lt;/p&gt;

&lt;p&gt;It is a hobby, a habit, and occasionally a headache I choose on purpose.&lt;/p&gt;

&lt;p&gt;And honestly, I mean that in a good way.&lt;/p&gt;

&lt;h2&gt;
  
  
  Final thought
&lt;/h2&gt;

&lt;p&gt;If you also keep building small local utility files for your own projects, I do not think that is a bad sign.&lt;/p&gt;

&lt;p&gt;Maybe it just means you are slowly learning what kind of friction bothers you, and what kind of tools help you think.&lt;/p&gt;

&lt;p&gt;That is more or less what this crate is for me.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>architecture</category>
      <category>productivity</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>My Tiny Rust Utils, Part 4: web.rs</title>
      <dc:creator>icsboyx</dc:creator>
      <pubDate>Thu, 30 Apr 2026 16:10:05 +0000</pubDate>
      <link>https://dev.to/icsboyx/my-tiny-rust-utils-part-4-webrs-4i1i</link>
      <guid>https://dev.to/icsboyx/my-tiny-rust-utils-part-4-webrs-4i1i</guid>
      <description>&lt;p&gt;I like &lt;code&gt;reqwest&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;I just do not always like seeing raw &lt;code&gt;reqwest&lt;/code&gt; patterns repeated all over my application code.&lt;/p&gt;

&lt;p&gt;That is the whole reason &lt;code&gt;src/utils/web.rs&lt;/code&gt; exists.&lt;/p&gt;

&lt;p&gt;I don't need full HTTP framework. I just need one place to keep a few recurring things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;response decoding&lt;/li&gt;
&lt;li&gt;basic header preparation&lt;/li&gt;
&lt;li&gt;a default &lt;code&gt;User-Agent&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;a simpler response type for the rest of the app&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A smaller response model
&lt;/h2&gt;

&lt;p&gt;The file starts here:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[derive(Debug,&lt;/span&gt; &lt;span class="nd"&gt;Clone,&lt;/span&gt; &lt;span class="nd"&gt;Serialize,&lt;/span&gt; &lt;span class="nd"&gt;Deserialize)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;ResponseBody&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nf"&gt;Binary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Bytes&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Debug,&lt;/span&gt; &lt;span class="nd"&gt;Clone)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;HttpReply&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;StatusCode&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;HeaderMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ResponseBody&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That already helps me.&lt;/p&gt;

&lt;p&gt;Instead of passing raw &lt;code&gt;reqwest::Response&lt;/code&gt; deeper into the app, I get a smaller local type that matches how this project actually thinks about HTTP replies.&lt;/p&gt;

&lt;p&gt;That is enough abstraction for me. No more is needed here.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;json_try_into&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The helper I probably use the most is this one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;ResponseBody&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;json_try_into&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'de&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Deserialize&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'de&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;ResponseBody&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;serde_json&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Into&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;bail!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Response body is not JSON, cannot deserialize"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That lets me write things like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;http_reply&lt;/span&gt;&lt;span class="py"&gt;.body.json_try_into&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BotIdentity&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I like this because it is strict in a simple, readable way.&lt;/p&gt;

&lt;p&gt;If the body is not JSON, it says so.&lt;/p&gt;

&lt;p&gt;If the JSON shape does not match the target type, it says so.&lt;/p&gt;

&lt;p&gt;That is exactly the level of honesty I want from utility code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Default headers without fuss
&lt;/h2&gt;

&lt;p&gt;The file also takes care of the default &lt;code&gt;User-Agent&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;DEFAULT_USER_AGENT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;concat!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;env!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CARGO_PKG_NAME"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;env!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"CARGO_PKG_VERSION"&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;prepare_headers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HeaderMap&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HeaderMap&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="nf"&gt;.unwrap_or_default&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="nf"&gt;.contains_key&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;header&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;USER_AGENT&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;HeaderValue&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_agent&lt;/span&gt;&lt;span class="nf"&gt;.unwrap_or&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DEFAULT_USER_AGENT&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="nf"&gt;.insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;header&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;USER_AGENT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a tiny thing, but it is one of those details that becomes annoying if I have to remember it in every call site.&lt;/p&gt;

&lt;p&gt;So I would rather centralize it once and stop thinking about it.&lt;/p&gt;

&lt;h2&gt;
  
  
  The public API stays small
&lt;/h2&gt;

&lt;p&gt;The actual entry points are just these:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;get_http&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="nb"&gt;AsRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HeaderMap&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user_agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpReply&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;prepare_headers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_agent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.headers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;apply_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.send&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;decode_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;post_http&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="nb"&gt;AsRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HeaderMap&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;[(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;user_agent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;HttpReply&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;prepare_headers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_agent&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;http_client&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.headers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;request&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="nf"&gt;.json&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;apply_query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.send&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nf"&gt;decode_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I am happy when a utility module stays about this size.&lt;/p&gt;

&lt;p&gt;It does not try to be clever.&lt;/p&gt;

&lt;p&gt;It just absorbs repetition so the rest of the code stays calmer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Where it pays off
&lt;/h2&gt;

&lt;p&gt;A real call site looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;sub_reply&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;post_http&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subscription_request&lt;/span&gt;&lt;span class="nf"&gt;.json_value&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nb"&gt;None&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
    &lt;span class="py"&gt;.body&lt;/span&gt;
    &lt;span class="nf"&gt;.json_try_into&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I still recognize that as ordinary application code.&lt;/p&gt;

&lt;p&gt;That is important to me.&lt;/p&gt;

&lt;p&gt;If the wrapper made this harder to read, I would not consider it a win.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving to Part 5
&lt;/h2&gt;

&lt;p&gt;At this point the pieces are all on the table:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;macros&lt;/li&gt;
&lt;li&gt;helpers&lt;/li&gt;
&lt;li&gt;save/load&lt;/li&gt;
&lt;li&gt;HTTP&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last part is the simplest file, &lt;code&gt;mod.rs&lt;/code&gt;, but it is also where I can finally say what this whole crate means to me.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>http</category>
      <category>api</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>My Tiny Rust Utils, Part 3: save_load.rs</title>
      <dc:creator>icsboyx</dc:creator>
      <pubDate>Thu, 30 Apr 2026 16:07:25 +0000</pubDate>
      <link>https://dev.to/icsboyx/my-tiny-rust-utils-part-3-saveloadrs-3e20</link>
      <guid>https://dev.to/icsboyx/my-tiny-rust-utils-part-3-saveloadrs-3e20</guid>
      <description>&lt;p&gt;Sooner or later, even a small bot needs to remember something.&lt;/p&gt;

&lt;p&gt;In my case, that usually means token-like or config-like data.&lt;/p&gt;

&lt;p&gt;I do not want to build a whole persistence subsystem for that, but I also do not want save/load code duplicated in random places.&lt;/p&gt;

&lt;p&gt;That is where &lt;code&gt;src/utils/save_load.rs&lt;/code&gt; comes from.&lt;/p&gt;

&lt;h2&gt;
  
  
  The basics
&lt;/h2&gt;

&lt;p&gt;The file starts with a few exports and a format enum:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;anyhow&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bail&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;serde&lt;/span&gt;&lt;span class="p"&gt;::{&lt;/span&gt;&lt;span class="n"&gt;Deserialize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Serialize&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="n"&gt;serde_json&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="n"&gt;toml&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="n"&gt;CONFIG_BASE_DIR&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;".config"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Debug,&lt;/span&gt; &lt;span class="nd"&gt;Clone,&lt;/span&gt; &lt;span class="nd"&gt;Copy,&lt;/span&gt; &lt;span class="nd"&gt;PartialEq,&lt;/span&gt; &lt;span class="nd"&gt;Eq,&lt;/span&gt; &lt;span class="nd"&gt;Default)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;SaveType&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;YAML&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;#[default]&lt;/span&gt;
    &lt;span class="n"&gt;TOML&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is a very local design.&lt;br&gt;
It is just trying to answer a smaller question:&lt;/p&gt;

&lt;p&gt;"How does this project save and load structured data without repeating itself?"&lt;/p&gt;

&lt;p&gt;For that, this is enough.&lt;/p&gt;
&lt;h2&gt;
  
  
  &lt;code&gt;ConfigManager&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The main trait is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;ConfigManager&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt;
    &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Sized&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;Serialize&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'de&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Deserialize&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'de&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;save_custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PathBuf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dir_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PathBuf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;save_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SaveType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;file_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;PathBuf&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;.join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dir_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.with_extension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;save_type&lt;/span&gt;&lt;span class="nf"&gt;.as_str&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="nf"&gt;save_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;save_type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;load_custom&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PathBuf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;dir_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PathBuf&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;save_type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;SaveType&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;file_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;PathBuf&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;.join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;dir_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.with_extension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;save_type&lt;/span&gt;&lt;span class="nf"&gt;.as_str&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="nf"&gt;load_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;save&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;struct_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;struct_name_from_type&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;file_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;PathBuf&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;.join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CONFIG_BASE_DIR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;struct_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.with_extension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;SaveType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.as_str&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="nf"&gt;save_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;SaveType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;struct_name&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;struct_name_from_type&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;file_path&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;PathBuf&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="nf"&gt;.join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;CONFIG_BASE_DIR&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.join&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;struct_name&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.with_extension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;SaveType&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;default&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.as_str&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="nf"&gt;load_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What I like here is not sophistication.&lt;/p&gt;

&lt;p&gt;It is that the feature code stays short.&lt;/p&gt;

&lt;p&gt;In the token flow, I can simply do:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;tw_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;TwitchToken&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;load_or_default&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;tw_token&lt;/span&gt;&lt;span class="nf"&gt;.save&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That feels about right for a project of this size.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that matters most to me
&lt;/h2&gt;

&lt;p&gt;The most important part of this file is not the serialization format.&lt;/p&gt;

&lt;p&gt;It is the path validation.&lt;/p&gt;

&lt;p&gt;Here is the real function:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;validate_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;Path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;anyhow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;PathBuf&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;normalized&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;PathBuf&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;comp&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;file_path&lt;/span&gt;&lt;span class="nf"&gt;.components&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;comp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nn"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Normal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;normalized&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;part&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nn"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CurDir&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
            &lt;span class="nn"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Prefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nd"&gt;bail!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Config path should not contain prefix component: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nn"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;RootDir&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nd"&gt;bail!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Config path should not contain root component: /.."&lt;/span&gt;&lt;span class="p"&gt;,);&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="nn"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ParentDir&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;normalized&lt;/span&gt;&lt;span class="nf"&gt;.pop&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="nd"&gt;bail!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                        &lt;span class="s"&gt;"Path traversal detected: config path should not contain parent directory component that goes beyond root dir"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;normalized&lt;/span&gt;&lt;span class="nf"&gt;.as_os_str&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.is_empty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;anyhow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;bail!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Empty config path"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;normalized&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I trust code more when the rules are visible.&lt;/p&gt;

&lt;p&gt;This function makes the rules visible:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;no absolute paths&lt;/li&gt;
&lt;li&gt;no prefix components&lt;/li&gt;
&lt;li&gt;no climbing upward beyond the normalized relative path&lt;/li&gt;
&lt;li&gt;no empty paths&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is the kind of small defensive behavior I like to centralize.&lt;/p&gt;

&lt;p&gt;Not because it is glamorous, but because I do not want to rediscover the same problem later.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I prefer this over inline file code
&lt;/h2&gt;

&lt;p&gt;Without a file like this, the same chores start leaking everywhere:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;picking the file extension&lt;/li&gt;
&lt;li&gt;creating directories&lt;/li&gt;
&lt;li&gt;converting values into the chosen format&lt;/li&gt;
&lt;li&gt;validating paths&lt;/li&gt;
&lt;li&gt;building consistent error messages&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I would rather make those choices once.&lt;/p&gt;

&lt;p&gt;This file is basically me admitting that repetitive file IO glue is not something I want to keep solving in five different places.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving to Part 4
&lt;/h2&gt;

&lt;p&gt;Once local persistence is under control, the next obvious pain point is external IO.&lt;/p&gt;

&lt;p&gt;For this project, that means HTTP.&lt;/p&gt;

&lt;p&gt;So the next part is &lt;code&gt;web.rs&lt;/code&gt;.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>config</category>
      <category>serde</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>My Tiny Rust Utils, Part 2: helpers.rs</title>
      <dc:creator>icsboyx</dc:creator>
      <pubDate>Thu, 30 Apr 2026 16:02:35 +0000</pubDate>
      <link>https://dev.to/icsboyx/my-tiny-rust-utils-part-2-helpersrs-5el0</link>
      <guid>https://dev.to/icsboyx/my-tiny-rust-utils-part-2-helpersrs-5el0</guid>
      <description>&lt;p&gt;After logging, the next kind of repetition that starts bothering me is usually this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;adding context to errors&lt;/li&gt;
&lt;li&gt;converting typed values into JSON request bodies&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neither of those is dramatic on its own.&lt;/p&gt;

&lt;p&gt;But if I leave them totally inline, they spread everywhere.&lt;/p&gt;

&lt;p&gt;That is why &lt;code&gt;src/utils/helpers.rs&lt;/code&gt; exists.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;WithLocation&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;This is the first helper:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;WithLocation&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;with_location&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;anyhow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;with_location_msg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;anyhow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;WithLocation&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;result&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt;
    &lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Into&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;anyhow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[track_caller]&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;with_location&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;anyhow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;loc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;caller&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Into&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.with_context&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}:{}:{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="nf"&gt;.file&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="nf"&gt;.line&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="nf"&gt;.column&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nd"&gt;#[track_caller]&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;with_location_msg&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;anyhow&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;loc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;caller&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.map_err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Into&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;into&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;.with_context&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{} at {}:{}:{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="nf"&gt;.file&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="nf"&gt;.line&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;loc&lt;/span&gt;&lt;span class="nf"&gt;.column&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I like this because it captures something I personally care about when debugging:&lt;/p&gt;

&lt;p&gt;not only what failed, but where I decided to wrap or reinterpret that failure.&lt;/p&gt;

&lt;p&gt;That is often the more useful location for me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I still keep it as a helper
&lt;/h2&gt;

&lt;p&gt;Yes, I could just write &lt;code&gt;.context(...)&lt;/code&gt; everywhere.&lt;/p&gt;

&lt;p&gt;Sometimes I do.&lt;/p&gt;

&lt;p&gt;But I know myself well enough: if the repetitive part feels slightly annoying, I will postpone it in one or two places, and then the code becomes uneven.&lt;/p&gt;

&lt;p&gt;This helper lowers that friction just enough that I actually use it.&lt;/p&gt;

&lt;p&gt;That is a recurring theme in this utils crate, honestly. I am not searching for elegance as much as I am trying to remove small excuses.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;IntoJsonValue&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The second helper is very small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;IntoJsonValue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;json_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;serde_json&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Value&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="k"&gt;where&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Sized&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;Serialize&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;serde_json&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;to_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;T&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;IntoJsonValue&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;T&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is almost embarrassingly tiny.&lt;/p&gt;

&lt;p&gt;Still, I like it.&lt;/p&gt;

&lt;p&gt;It lets me write:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subscription_request&lt;/span&gt;&lt;span class="nf"&gt;.json_value&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;instead of:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;serde_json&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;to_value&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;subscription_request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That difference is not huge.&lt;/p&gt;

&lt;p&gt;But I think a lot of utility code lives exactly in that zone: not essential, not magical, just a little nicer to use.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;TrackError&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;The same file also has a very small custom error type:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[derive(Debug)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;TrackError&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;'static&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Location&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;'static&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;TrackError&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[track_caller]&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="nb"&gt;Into&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;Self&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;location&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;panic&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Location&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;caller&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I keep it around because sometimes I want something between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a full error enum&lt;/li&gt;
&lt;li&gt;and a generic error chain&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sometimes I just want a normal error value with a message and a caller location.&lt;/p&gt;

&lt;p&gt;This gives me that, without pretending to be more important than it is.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving to Part 3
&lt;/h2&gt;

&lt;p&gt;Once logs and helper traits are in place, the next problem is usually state.&lt;/p&gt;

&lt;p&gt;Small apps still need to save things. Tokens, config-like values, maybe a bit of local persistence.&lt;/p&gt;

&lt;p&gt;That is where &lt;code&gt;save_load.rs&lt;/code&gt; comes in.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>errors</category>
      <category>serde</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>My Tiny Rust Utils, Part 1: macros.rs</title>
      <dc:creator>icsboyx</dc:creator>
      <pubDate>Thu, 30 Apr 2026 16:01:19 +0000</pubDate>
      <link>https://dev.to/icsboyx/my-tiny-rust-utils-part-1-macrosrs-1m7b</link>
      <guid>https://dev.to/icsboyx/my-tiny-rust-utils-part-1-macrosrs-1m7b</guid>
      <description>&lt;p&gt;I did not build this little &lt;code&gt;utils&lt;/code&gt; crate because I thought I had something revolutionary to show.&lt;/p&gt;

&lt;p&gt;I built it for a much simpler reason: while working on small Rust projects, I keep running into the same tiny frictions over and over again.&lt;/p&gt;

&lt;p&gt;Nothing dramatic. Just the usual stuff:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;logging that starts inconsistent&lt;/li&gt;
&lt;li&gt;little helper code repeated in the wrong places&lt;/li&gt;
&lt;li&gt;config or HTTP glue leaking into feature modules&lt;/li&gt;
&lt;li&gt;small annoyances that are easy to ignore for a day and then annoying for a month&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So this crate is mostly me trying to be a bit kinder to my future self.&lt;/p&gt;

&lt;p&gt;I wanted one place for the small things that make the project easier to read, easier to debug, and a little less noisy.&lt;/p&gt;

&lt;p&gt;And I wanted to share it for the exact same reason.&lt;/p&gt;

&lt;p&gt;Not because these files are perfect, and not because I think anyone should copy them as-is, but because I always enjoy seeing how other people solve these very ordinary problems in their own projects.&lt;/p&gt;

&lt;p&gt;Sometimes the most useful things to share are not big architectures or polished libraries.&lt;/p&gt;

&lt;p&gt;Sometimes they are just small, honest utilities that made a project nicer to work on.&lt;/p&gt;

&lt;p&gt;This is the crate I am talking about:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;src/
└── utils/
    ├── helpers.rs
    ├── macros.rs
    ├── mod.rs
    ├── save_load.rs
    └── web.rs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So before getting into the rest of the series, I wanted to show the shape of it.&lt;/p&gt;

&lt;p&gt;It is a small crate, but each file has its own job:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;macros.rs&lt;/code&gt; for logs, timestamps, and source breadcrumbs&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;helpers.rs&lt;/code&gt; for small error and JSON helpers&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;save_load.rs&lt;/code&gt; for config-like persistence&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;web.rs&lt;/code&gt; for a thin HTTP layer&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;mod.rs&lt;/code&gt; as the glue that exposes the submodules&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If I had to choose where this little series really starts, it would be &lt;code&gt;macros.rs&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Not because macros are the most important thing in the world, and not because this file is especially smart.&lt;/p&gt;

&lt;p&gt;It is just the first file that makes the rest of the project easier to live with.&lt;/p&gt;

&lt;p&gt;When I am building a small bot or service, I usually want a few very basic things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;log lines that look consistent&lt;/li&gt;
&lt;li&gt;timestamps without repeating formatting code everywhere&lt;/li&gt;
&lt;li&gt;a cheap way to see where something happened&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That is all &lt;code&gt;src/utils/macros.rs&lt;/code&gt; is trying to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  The real &lt;code&gt;log!&lt;/code&gt; macro
&lt;/h2&gt;

&lt;p&gt;Here is the actual code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[macro_export]&lt;/span&gt;
&lt;span class="nd"&gt;macro_rules!&lt;/span&gt; &lt;span class="k"&gt;log&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;
        &lt;span class="nv"&gt;$crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;log!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}};&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$arg:tt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;
        &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;println!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"[ {} ] {} {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nv"&gt;$crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;timestamp!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;millis&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nd"&gt;cfg!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debug_assertions&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;here!&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[ {:&amp;lt;20} ]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;module_path!&lt;/span&gt;&lt;span class="p"&gt;())},&lt;/span&gt;
            &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$arg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the stderr version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[macro_export]&lt;/span&gt;
&lt;span class="nd"&gt;macro_rules!&lt;/span&gt; &lt;span class="n"&gt;err_log&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;
        &lt;span class="nv"&gt;$crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;err_log!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}};&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$arg:tt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt;
        &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"[ {} ] {} {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nv"&gt;$crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;timestamp!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;millis&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nd"&gt;cfg!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debug_assertions&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;here!&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[ {:&amp;lt;20} ]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;module_path!&lt;/span&gt;&lt;span class="p"&gt;())},&lt;/span&gt;
            &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$arg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is not a grand logging system.&lt;/p&gt;

&lt;p&gt;It is just a format I do not have to think about every time I want to print something.&lt;/p&gt;

&lt;p&gt;That matters more than it sounds.&lt;/p&gt;

&lt;p&gt;When the friction is low, I log more consistently. When I log more consistently, I understand the project better.&lt;/p&gt;

&lt;h2&gt;
  
  
  The small trick I still like
&lt;/h2&gt;

&lt;p&gt;This part is probably my favorite:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nd"&gt;cfg!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;debug_assertions&lt;/span&gt;&lt;span class="p"&gt;){&lt;/span&gt;&lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;here!&lt;/span&gt;&lt;span class="p"&gt;()}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[ {:&amp;lt;20} ]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;module_path!&lt;/span&gt;&lt;span class="p"&gt;())}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In debug builds, I get source position.&lt;/p&gt;

&lt;p&gt;In release builds, I get the module path instead.&lt;/p&gt;

&lt;p&gt;That feels modest, but useful:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;during development I want precise breadcrumbs&lt;/li&gt;
&lt;li&gt;outside development I usually want something a bit cleaner&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I do not need this to be perfect. I just need it to help.&lt;/p&gt;

&lt;h2&gt;
  
  
  &lt;code&gt;here!&lt;/code&gt; is tiny, but honest
&lt;/h2&gt;

&lt;p&gt;This is the real macro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[macro_export]&lt;/span&gt;
&lt;span class="nd"&gt;macro_rules!&lt;/span&gt; &lt;span class="n"&gt;here&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"[ {:&amp;lt;30} ]"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}:{}:{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;file!&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nd"&gt;line!&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nd"&gt;column!&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fmt:literal&lt;/span&gt; &lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(,&lt;/span&gt; &lt;span class="nv"&gt;$arg:expr&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(,)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"[ {:&amp;lt;30} ] {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nd"&gt;format!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}:{}:{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;file!&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nd"&gt;line!&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nd"&gt;column!&lt;/span&gt;&lt;span class="p"&gt;()),&lt;/span&gt;
            &lt;span class="nd"&gt;format_args!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fmt&lt;/span&gt; &lt;span class="nv"&gt;$&lt;/span&gt;&lt;span class="p"&gt;(,&lt;/span&gt; &lt;span class="nv"&gt;$arg&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I use it directly in places like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;session_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;msg&lt;/span&gt;&lt;span class="py"&gt;.payload.session&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;here!&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="py"&gt;.id&lt;/span&gt;&lt;span class="nf"&gt;.as_str&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That line is not fancy at all. It is basically me saying:&lt;/p&gt;

&lt;p&gt;"If this breaks, please show me exactly where I made this assumption."&lt;/p&gt;

&lt;p&gt;I like helpers that are honest like that.&lt;/p&gt;

&lt;h2&gt;
  
  
  Timestamp helpers
&lt;/h2&gt;

&lt;p&gt;The timestamp macro is also deliberately small:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[macro_export]&lt;/span&gt;
&lt;span class="nd"&gt;macro_rules!&lt;/span&gt; &lt;span class="n"&gt;timestamp&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nn"&gt;chrono&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Local&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%Y-%m-%d %H:%M:%S"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}};&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;milliseconds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nn"&gt;chrono&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Local&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%Y-%m-%d %H:%M:%S%.3f"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}};&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;millis&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;$crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;timestamp!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;milliseconds&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}};&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;micro&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nn"&gt;chrono&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Local&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%Y-%m-%d %H:%M:%S%.6f"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}};&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;micros&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;$crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;timestamp!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;micro&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}};&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nano&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nn"&gt;chrono&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Local&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"%Y-%m-%d %H:%M:%S%.9f"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}};&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nanos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;$crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nd"&gt;timestamp!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nano&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}};&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fmt:literal&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{{&lt;/span&gt; &lt;span class="nn"&gt;chrono&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Local&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$fmt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}};&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$unknown:tt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;compile_error!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="s"&gt;"unsupported timestamp! argument. Use: milliseconds|millis|micro|micros|nano|nanos|&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;custom fmt&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I am aware this is not revolutionary code.&lt;/p&gt;

&lt;p&gt;That is kind of the point.&lt;/p&gt;

&lt;p&gt;It saves me from repeating &lt;code&gt;chrono&lt;/code&gt; formatting noise, and it keeps the call sites readable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;log&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Token will expire at: {:#?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;TW_TOKEN&lt;/span&gt;&lt;span class="nf"&gt;.expires_at&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For this project, that is enough justification.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the output looks like
&lt;/h2&gt;

&lt;p&gt;In debug builds, the output includes the exact source location:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ 2026-04-30 12:15:44.381 ] [ src/tw_client.rs:94:36       ] Handling session welcome message, id abc123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In release builds, the same kind of line becomes a bit cleaner and shows the module path instead:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ 2026-04-30 12:15:44.381 ] [ botox::tw_client     ] Handling session welcome message, id abc123
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That difference is small, but it is intentional.&lt;/p&gt;

&lt;p&gt;When I am debugging, I usually want the exact place.&lt;/p&gt;

&lt;p&gt;When I am not, the module name is often enough.&lt;/p&gt;

&lt;p&gt;It is not beautiful. It is not trying to be.&lt;/p&gt;

&lt;p&gt;It is just useful in the exact way I need.&lt;/p&gt;

&lt;h2&gt;
  
  
  Moving to Part 2
&lt;/h2&gt;

&lt;p&gt;Once I have logs and source breadcrumbs, the next annoyance is usually error context and JSON glue.&lt;/p&gt;

&lt;p&gt;That is where &lt;code&gt;helpers.rs&lt;/code&gt; starts to matter, so that is the next part.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>debugging</category>
      <category>logging</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
