<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>NullDeref</title>
  <subtitle>Mario Ortiz Manero&#39;s personal blog with his life learnings.</subtitle>
  <link href="https://nullderef.com/index.xml" rel="self" />
  <link href="https://nullderef.com/" />
  <updated>2025-04-08T00:00:00Z</updated>
  <id>https://nullderef.com/</id>
  <author>
    <name>Mario Ortiz Manero</name>
    <email>marioortizmanero@gmail.com</email>
  </author>
  <entry>
    <title>Releasing SpeedBump on the Play Store</title>
    <link href="https://nullderef.com/blog/speedbump-play-store-release/" />
    <updated>2025-04-08T00:00:00Z</updated>
    <id>https://nullderef.com/blog/speedbump-play-store-release/</id>
    <content type="html"></content>
  </entry>
  <entry>
    <title>Lessons on Mobile App Promotion I&#39;ve Learned the Hard Way</title>
    <link href="https://nullderef.com/blog/speedbump-mobile-app-promotion/" />
    <updated>2025-04-02T00:00:00Z</updated>
    <id>https://nullderef.com/blog/speedbump-mobile-app-promotion/</id>
    <content type="html"></content>
  </entry>
  <entry>
    <title>Screen Time App Opal Jokes about Being Bought by Meta</title>
    <link href="https://nullderef.com/blog/speedbump-opal-bought-by-meta/" />
    <updated>2025-04-01T00:00:00Z</updated>
    <id>https://nullderef.com/blog/speedbump-opal-bought-by-meta/</id>
    <content type="html"></content>
  </entry>
  <entry>
    <title>How to Reduce Screen Time: The Ultimate Guide</title>
    <link href="https://nullderef.com/blog/speedbump-social-media-timer/" />
    <updated>2025-03-07T00:00:00Z</updated>
    <id>https://nullderef.com/blog/speedbump-social-media-timer/</id>
    <content type="html"></content>
  </entry>
  <entry>
    <title>The German language broke my website</title>
    <link href="https://nullderef.com/blog/speedbump-german-translation/" />
    <updated>2025-03-04T00:00:00Z</updated>
    <id>https://nullderef.com/blog/speedbump-german-translation/</id>
    <content type="html"></content>
  </entry>
  <entry>
    <title>My app sucked (but it&#39;s okay)</title>
    <link href="https://nullderef.com/blog/speedbump-v1/" />
    <updated>2025-02-26T00:00:00Z</updated>
    <id>https://nullderef.com/blog/speedbump-v1/</id>
    <content type="html"></content>
  </entry>
  <entry>
    <title>I blasted over a speed bump to create my landing page</title>
    <link href="https://nullderef.com/blog/speedbump-launch/" />
    <updated>2025-02-14T00:00:00Z</updated>
    <id>https://nullderef.com/blog/speedbump-launch/</id>
    <content type="html"></content>
  </entry>
  <entry>
    <title>New year, new silly app</title>
    <link href="https://nullderef.com/blog/phone-jan25/" />
    <updated>2025-01-20T00:00:00Z</updated>
    <id>https://nullderef.com/blog/phone-jan25/</id>
    <content type="html">&lt;p&gt;So far, the year is off to a great start. New Year’s resolutions aren’t my thing, but this time I’ve made an exception. Not so much with specific objectives but rather broad goals. The biggest one is to be more mindful. I want to observe more, reflect, and act:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Sport does me so much good, but in 2024, planning it was a mess. Maybe a swimming club will make it easy to go regularly, for fun. Having a strict schedule also helps keep my self-employed life under control.&lt;/li&gt;
&lt;li&gt;Nutrition is crucial if I do more sport. No need to obsess over this, just being more mindful helps.&lt;/li&gt;
&lt;li&gt;Reading taught me a lot in 2024. My flat now has a shelf of never-ending unread books for motivation.&lt;/li&gt;
&lt;li&gt;The best way to maintain good mental health is to be surrounded by great people. Caring for the valuable relationships makes a big difference.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Such conclusions can only be achieved by sitting down and thinking. I’m both changing my mindset and incorporating new routines. Just keeping in mind the fact that I need to be mindful already has an effect. It’s easy to be busy all the time, but my brain now sometimes tells me to stop for a second. Additionally, things like “&lt;a href=&quot;https://www.theguardian.com/lifeandstyle/2014/oct/03/morning-pages-change-your-life-oliver-burkeman&quot;&gt;morning pages&lt;/a&gt;”, meditation and simple prioritization help.&lt;/p&gt;
&lt;p&gt;I’m also happy to have &lt;a href=&quot;https://nullderef.com/blog/quit-job-2024&quot;&gt;quit my job&lt;/a&gt;. It’s been quite chaotic, but every step back has been a lesson. Even though I’m still at the start line, my understanding of the problem is more clear now:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Working with so much ambiguity is tough. But my ideas are now more viable and concrete. I don’t want to build either a new phone or yet another app-blocking app with a bazillion features.&lt;/li&gt;
&lt;li&gt;Finding a cofounder is equally challenging. Meeting so many people has taught me what a good match looks like. And it’s great to have the contact of such cool individuals for other reasons.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Surprisingly, it’s less stressful. I’m definitely speaking too early, given that my user count is 0. But having control over my schedule gives me peace of mind. The routine feels similar because I still have to do things I don’t like. But the product I’m striving towards is fulfilling, and that makes up for it.&lt;/p&gt;
&lt;p&gt;Before committing to a bigger idea, I’m building a silly Android app. “SpeedBump” makes it annoying to use social media and other apps. After a few minutes, cookie banners, rain effects, cats, and more will get in your way. It’s as stupid as it sounds, but also fun. &lt;strong&gt;Google now requires developers to find 12 people to test the app, so &lt;a href=&quot;https://forms.gle/3p5mgZcAFJcwtcuh7&quot;&gt;please opt in here to help me out&lt;/a&gt;!&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;More serious stuff is coming soon, anyhow. I hope SpeedBump isn’t the peak of my career.&lt;/p&gt;

&lt;script src=&quot;https://f.convertkit.com/ckjs/ck.5.js&quot;&gt;&lt;/script&gt;
&lt;form action=&quot;https://app.kit.com/forms/7534343/subscriptions&quot; class=&quot;seva-form formkit-form&quot; method=&quot;post&quot; data-sv-form=&quot;7534343&quot; data-uid=&quot;3680381778&quot; data-format=&quot;inline&quot; data-version=&quot;5&quot; data-options=&#39;{&quot;settings&quot;:{&quot;after_subscribe&quot;:{&quot;action&quot;:&quot;message&quot;,&quot;success_message&quot;:&quot;Success! Check your email to confirm your subscription. If you can’t find it, look in the spam folder.&quot;,&quot;redirect_url&quot;:&quot;&quot;},&quot;analytics&quot;:{&quot;google&quot;:null,&quot;fathom&quot;:null,&quot;facebook&quot;:null,&quot;segment&quot;:null,&quot;pinterest&quot;:null,&quot;sparkloop&quot;:null,&quot;googletagmanager&quot;:null},&quot;modal&quot;:{&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;powered_by&quot;:{&quot;show&quot;:true,&quot;url&quot;:&quot;https://kit.com/features/forms?utm_campaign=poweredby&amp;amp;utm_content=form&amp;amp;utm_medium=referral&amp;amp;utm_source=dynamic&quot;},&quot;recaptcha&quot;:{&quot;enabled&quot;:false},&quot;return_visitor&quot;:{&quot;action&quot;:&quot;show&quot;,&quot;custom_content&quot;:&quot;&quot;},&quot;slide_in&quot;:{&quot;display_in&quot;:&quot;bottom_right&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;sticky_bar&quot;:{&quot;display_in&quot;:&quot;top&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15}},&quot;version&quot;:&quot;5&quot;}&#39; min-width=&quot;400 500 600 700 800&quot;&gt;
  &lt;div data-style=&quot;clean&quot;&gt;
    &lt;div class=&quot;formkit-header&quot; data-element=&quot;header&quot;&gt;
      &lt;h2&gt;Want to receive an email when I post?&lt;/h2&gt;
    &lt;/div&gt;
    &lt;ul class=&quot;formkit-alert formkit-alert-error&quot; data-element=&quot;errors&quot; data-group=&quot;alert&quot;&gt;&lt;/ul&gt;
    &lt;div data-element=&quot;fields&quot; data-stacked=&quot;false&quot; class=&quot;seva-fields formkit-fields&quot;&gt;
      &lt;div class=&quot;formkit-field&quot;&gt;
        &lt;input class=&quot;formkit-input&quot; name=&quot;email_address&quot; aria-label=&quot;Email Address&quot; placeholder=&quot;Email Address&quot; required=&quot;&quot; type=&quot;email&quot;&gt;
      &lt;/div&gt;
      &lt;button data-element=&quot;submit&quot; class=&quot;formkit-submit&quot; style=&quot;background-color:#e06c75;font-weight:700&quot;&gt;
        &lt;div class=&quot;formkit-spinner&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span class=&quot;&quot;&gt;Subscribe&lt;/span&gt;
      &lt;/button&gt;
    &lt;/div&gt;
    &lt;div class=&quot;formkit-guarantee&quot; data-element=&quot;guarantee&quot;&gt;
      &lt;p&gt;&lt;em&gt;Free and without spam. You can also use my &lt;a href=&quot;https://nullderef.com/index.xml&quot; target=&quot;_blank&quot;&gt;RSS&lt;/a&gt;, &lt;a href=&quot;https://x.com/null_deref&quot; target=&quot;_blank&quot;&gt;X&lt;/a&gt;, or &lt;a href=&quot;https://mstdn.social/@nullderef&quot; target=&quot;_blank&quot;&gt;Mastodon&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
      &lt;p style=&quot;opacity:.6;font-size:16px&quot;&gt;&lt;em&gt;Powered by &lt;a href=&quot;https://kit.com/&quot; target=&quot;_black&quot;&gt;Kit&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;style&gt;.formkit-form[data-uid=&quot;3680381778&quot;] a{text-decoration:underline}.formkit-form[data-uid=&quot;3680381778&quot;] legend{border:none;font-size:inherit;margin-bottom:10px;padding:0;position:relative;display:table}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-checkboxes,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-select{width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{border:0;border-radius:var(--radius);color:#fff;cursor:pointer;display:inline-block;text-align:center;font-weight:500;cursor:pointer;margin-bottom:15px;overflow:hidden;padding:0;position:relative;vertical-align:middle}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover{outline:0}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover&gt;span{background-color:rgba(0,0,0,.1)}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit&gt;span{display:block;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out;padding:12px 24px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input{background:#fff;padding:12px;border:1px solid var(--border);border-radius:var(--radius);-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;line-height:1.4;margin:0;-webkit-transition:border-color ease-out .3s;transition:border-color ease-out .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:focus{outline:0;border-color:#1677be;-webkit-transition:border-color ease .3s;transition:border-color ease .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-webkit-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-moz-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:-ms-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert{background:#f9fafb;border:1px solid #e3e3e3;border-radius:5px;-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;list-style:none;margin:25px auto;padding:12px;text-align:center;width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert:empty{display:none}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-success{background:#d3fbeb;border-color:#10bf7a;color:#0c905c}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-error{background:#fde8e2;border-color:#f2643b;color:#ea4110}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;height:0;width:0;margin:0 auto;position:absolute;top:0;left:0;right:0;width:0;overflow:hidden;text-align:center;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div{margin:auto;width:12px;height:12px;background-color:#fff;opacity:.3;border-radius:100%;display:inline-block;-webkit-animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both;animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:first-child{-webkit-animation-delay:-.32s;animation-delay:-.32s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:nth-child(2){-webkit-animation-delay:-.16s;animation-delay:-.16s}.formkit-form[data-uid=&quot;3680381778&quot;]{border:1px solid var(--border);border-radius:var(--radius);position:relative;overflow:hidden;background:var(--code-bg)}.formkit-form[data-uid=&quot;3680381778&quot;]{padding:20px;width:100%;position:relative}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-header{margin:0 0 27px 0;font-size:20px;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee{margin-top:10px;margin-bottom:32px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee&gt;p{margin:0;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-fields{display:flex;flex-wrap:wrap;margin:25px auto 0 auto;gap:10px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-field,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{flex:1 0 100%}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;]{padding:0 30px}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-field{flex:100 1 auto}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-submit{flex:1 1 auto}&lt;/style&gt;
&lt;/form&gt;
</content>
  </entry>
  <entry>
    <title>I&#39;ve quit my job to get you off your phone</title>
    <link href="https://nullderef.com/blog/quit-job-2024/" />
    <updated>2024-10-14T00:00:00Z</updated>
    <id>https://nullderef.com/blog/quit-job-2024/</id>
    <content type="html">&lt;p&gt;I never expected quitting a job to be so hard! My time at Lyft has been an enriching journey. Tons of smart people at three very different teams, where I’ve learned how the industry works. The technical challenges were endless: from user-facing features in the app to improving the safety of &lt;a href=&quot;https://www.lyft.com/blog/posts/lyfts-secret-plan-to-take-control-of-its-maps-and-its-future&quot;&gt;our routing&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;But after two years, I’ve realized that it just wasn’t making me happy. My disconnect with the product was too big, and so much remote work didn’t help, either. The desire to explore grew, with the hope that something else could bring more fulfillment.&lt;/p&gt;
&lt;p&gt;I’ve always aspired to build a company, and this was a good moment in life to try. Now or never. So here I am, soon to be jobless, contemplating life, yet excited about what’s next.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/img/M2zVw-0wTM-1000.avif 1000w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/img/M2zVw-0wTM-1000.webp 1000w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://nullderef.com/img/M2zVw-0wTM-1000.png&quot; alt=&quot;Picturing how I feel&quot; width=&quot;1000&quot; height=&quot;400&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;My company might fail (in fact, the odds are against me). But I’ll have tried and learned! This decision reassures me I’m making the best out of life, and it frees me of regrets. Mostly. It’s still pretty insane that I quit.&lt;/p&gt;
&lt;p&gt;It’s early in my journey, but I can’t stop exploring my area of interest: phones. Or rather, phonesn’t. Not having had the best year of my life, I found myself on the phone too much. The negative effects became obvious, and I want to do something about it.&lt;/p&gt;
&lt;p&gt;This space is quite a challenge. Everyone overuses their phone in their own way. You have big companies &lt;em&gt;optimizing&lt;/em&gt; their app to get you stuck on it. And often, overuse is a symptom of a deeper problem, where you’d be better off directly
fixing that.&lt;/p&gt;
&lt;p&gt;Yet, it’s such a huge issue that I’m confident I can build something valuable. You don’t need to be completely addicted to your phone to want to use it less. Among the thousand possible ideas — even &lt;a href=&quot;https://nullderef.com/blog/phone-intro/&quot;&gt;manufacturing your own phone&lt;/a&gt; —, I might get started with an innovative app to control screentime. It’s frustrating that no existing apps have gone mainstream. Let’s make it fun to reduce your usage.&lt;/p&gt;


&lt;p&gt;
  &lt;iframe loading=&quot;lazy&quot; src=&quot;https://docs.google.com/forms/d/e/1FAIpQLSeBH5uuC0SobGZemcapCMxcKkHgL49l0wvtYTQjG3M-puAMew/viewform?embedded=true&quot; width=&quot;100%&quot; height=&quot;410&quot; frameborder=&quot;0&quot; marginheight=&quot;0&quot; marginwidth=&quot;0&quot;&gt;Loading…&lt;/iframe&gt;
&lt;/p&gt;
&lt;p&gt;If this is interesting to you in any way, please reach out! &lt;a href=&quot;https://chat.nullderef.com&quot;&gt;Schedule a chat with me&lt;/a&gt; or send me a message on &lt;a href=&quot;https://www.linkedin.com/in/marioortizmanero/&quot;&gt;LinkedIn&lt;/a&gt;, &lt;a href=&quot;https://x.com/null_deref&quot;&gt;X&lt;/a&gt;, &lt;a href=&quot;https://mstdn.social/@nullderef&quot;&gt;Mastodon&lt;/a&gt;, or by &lt;a href=&quot;mailto:marioortizmanero@gmail.com&quot;&gt;email&lt;/a&gt;. You can vent about how much your phone controls you, share someone you know, or we could work together. Even catching up is a good reason :)&lt;/p&gt;
&lt;p&gt;I feel extremely lucky to be in a position to make this decision. Thank you to everyone for your support!&lt;/p&gt;

&lt;script src=&quot;https://f.convertkit.com/ckjs/ck.5.js&quot;&gt;&lt;/script&gt;
&lt;form action=&quot;https://app.kit.com/forms/7534343/subscriptions&quot; class=&quot;seva-form formkit-form&quot; method=&quot;post&quot; data-sv-form=&quot;7534343&quot; data-uid=&quot;3680381778&quot; data-format=&quot;inline&quot; data-version=&quot;5&quot; data-options=&#39;{&quot;settings&quot;:{&quot;after_subscribe&quot;:{&quot;action&quot;:&quot;message&quot;,&quot;success_message&quot;:&quot;Success! Check your email to confirm your subscription. If you can’t find it, look in the spam folder.&quot;,&quot;redirect_url&quot;:&quot;&quot;},&quot;analytics&quot;:{&quot;google&quot;:null,&quot;fathom&quot;:null,&quot;facebook&quot;:null,&quot;segment&quot;:null,&quot;pinterest&quot;:null,&quot;sparkloop&quot;:null,&quot;googletagmanager&quot;:null},&quot;modal&quot;:{&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;powered_by&quot;:{&quot;show&quot;:true,&quot;url&quot;:&quot;https://kit.com/features/forms?utm_campaign=poweredby&amp;amp;utm_content=form&amp;amp;utm_medium=referral&amp;amp;utm_source=dynamic&quot;},&quot;recaptcha&quot;:{&quot;enabled&quot;:false},&quot;return_visitor&quot;:{&quot;action&quot;:&quot;show&quot;,&quot;custom_content&quot;:&quot;&quot;},&quot;slide_in&quot;:{&quot;display_in&quot;:&quot;bottom_right&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;sticky_bar&quot;:{&quot;display_in&quot;:&quot;top&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15}},&quot;version&quot;:&quot;5&quot;}&#39; min-width=&quot;400 500 600 700 800&quot;&gt;
  &lt;div data-style=&quot;clean&quot;&gt;
    &lt;div class=&quot;formkit-header&quot; data-element=&quot;header&quot;&gt;
      &lt;h2&gt;Want to receive an email when I post?&lt;/h2&gt;
    &lt;/div&gt;
    &lt;ul class=&quot;formkit-alert formkit-alert-error&quot; data-element=&quot;errors&quot; data-group=&quot;alert&quot;&gt;&lt;/ul&gt;
    &lt;div data-element=&quot;fields&quot; data-stacked=&quot;false&quot; class=&quot;seva-fields formkit-fields&quot;&gt;
      &lt;div class=&quot;formkit-field&quot;&gt;
        &lt;input class=&quot;formkit-input&quot; name=&quot;email_address&quot; aria-label=&quot;Email Address&quot; placeholder=&quot;Email Address&quot; required=&quot;&quot; type=&quot;email&quot;&gt;
      &lt;/div&gt;
      &lt;button data-element=&quot;submit&quot; class=&quot;formkit-submit&quot; style=&quot;background-color:#e06c75;font-weight:700&quot;&gt;
        &lt;div class=&quot;formkit-spinner&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span class=&quot;&quot;&gt;Subscribe&lt;/span&gt;
      &lt;/button&gt;
    &lt;/div&gt;
    &lt;div class=&quot;formkit-guarantee&quot; data-element=&quot;guarantee&quot;&gt;
      &lt;p&gt;&lt;em&gt;Free and without spam. You can also use my &lt;a href=&quot;https://nullderef.com/index.xml&quot; target=&quot;_blank&quot;&gt;RSS&lt;/a&gt;, &lt;a href=&quot;https://x.com/null_deref&quot; target=&quot;_blank&quot;&gt;X&lt;/a&gt;, or &lt;a href=&quot;https://mstdn.social/@nullderef&quot; target=&quot;_blank&quot;&gt;Mastodon&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
      &lt;p style=&quot;opacity:.6;font-size:16px&quot;&gt;&lt;em&gt;Powered by &lt;a href=&quot;https://kit.com/&quot; target=&quot;_black&quot;&gt;Kit&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;style&gt;.formkit-form[data-uid=&quot;3680381778&quot;] a{text-decoration:underline}.formkit-form[data-uid=&quot;3680381778&quot;] legend{border:none;font-size:inherit;margin-bottom:10px;padding:0;position:relative;display:table}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-checkboxes,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-select{width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{border:0;border-radius:var(--radius);color:#fff;cursor:pointer;display:inline-block;text-align:center;font-weight:500;cursor:pointer;margin-bottom:15px;overflow:hidden;padding:0;position:relative;vertical-align:middle}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover{outline:0}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover&gt;span{background-color:rgba(0,0,0,.1)}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit&gt;span{display:block;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out;padding:12px 24px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input{background:#fff;padding:12px;border:1px solid var(--border);border-radius:var(--radius);-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;line-height:1.4;margin:0;-webkit-transition:border-color ease-out .3s;transition:border-color ease-out .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:focus{outline:0;border-color:#1677be;-webkit-transition:border-color ease .3s;transition:border-color ease .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-webkit-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-moz-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:-ms-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert{background:#f9fafb;border:1px solid #e3e3e3;border-radius:5px;-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;list-style:none;margin:25px auto;padding:12px;text-align:center;width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert:empty{display:none}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-success{background:#d3fbeb;border-color:#10bf7a;color:#0c905c}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-error{background:#fde8e2;border-color:#f2643b;color:#ea4110}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;height:0;width:0;margin:0 auto;position:absolute;top:0;left:0;right:0;width:0;overflow:hidden;text-align:center;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div{margin:auto;width:12px;height:12px;background-color:#fff;opacity:.3;border-radius:100%;display:inline-block;-webkit-animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both;animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:first-child{-webkit-animation-delay:-.32s;animation-delay:-.32s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:nth-child(2){-webkit-animation-delay:-.16s;animation-delay:-.16s}.formkit-form[data-uid=&quot;3680381778&quot;]{border:1px solid var(--border);border-radius:var(--radius);position:relative;overflow:hidden;background:var(--code-bg)}.formkit-form[data-uid=&quot;3680381778&quot;]{padding:20px;width:100%;position:relative}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-header{margin:0 0 27px 0;font-size:20px;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee{margin-top:10px;margin-bottom:32px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee&gt;p{margin:0;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-fields{display:flex;flex-wrap:wrap;margin:25px auto 0 auto;gap:10px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-field,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{flex:1 0 100%}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;]{padding:0 30px}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-field{flex:100 1 auto}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-submit{flex:1 1 auto}&lt;/style&gt;
&lt;/form&gt;
</content>
  </entry>
  <entry>
    <title>Blindsided by Rust&#39;s Subtyping and Variance</title>
    <link href="https://nullderef.com/blog/rust-variance/" />
    <updated>2024-09-14T00:00:00Z</updated>
    <id>https://nullderef.com/blog/rust-variance/</id>
    <content type="html">&lt;details class=&quot;toc&quot;&gt;
&lt;summary&gt;Table of Contents&lt;/summary&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-variance/#the-problem&quot;&gt;The problem &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-variance/#debugging&quot;&gt;Debugging &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-variance/#some-progress-or-not&quot;&gt;Some progress… or not? &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-variance/#discovering-the-root-cause&quot;&gt;Discovering the root cause &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-variance/#trying-to-fix-it&quot;&gt;Trying to fix it &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-variance/#attempt-1-gats&quot;&gt;Attempt #1: GATs &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-variance/#attempt-2-transmute&quot;&gt;Attempt #2: transmute &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-variance/#attempt-3-getting-rid-of-borrowowned-a&quot;&gt;Attempt #3: getting rid of BorrowOwned&amp;lt;&#39;a&amp;gt; &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-variance/#conclusion&quot;&gt;Conclusion &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/details&gt;
&lt;p&gt;&lt;em&gt;Subtyping and variance&lt;/em&gt; is a concept that works in the background, making your life easier without you knowing about it. That is, until it starts making your life harder instead. It’s a good idea to know about it, in case you end up being a fool like me. So let’s take a look at what went wrong, and how it was resolved.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_the_problem&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-problem&quot; tabindex=&quot;-1&quot;&gt;The problem &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-variance/#the-problem&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As part of my &lt;a href=&quot;https://nullderef.com/series/rust-plugins/&quot;&gt;Plugin System in Rust&lt;/a&gt; series, I was making one of &lt;a href=&quot;https://www.tremor.rs/&quot;&gt;Tremor&lt;/a&gt;’s types FFI-compatible. Put simply, instead of using types from the standard library like &lt;code&gt;String&lt;/code&gt;, we wanted custom types defined &lt;a href=&quot;https://doc.rust-lang.org/nomicon/other-reprs.html#reprc&quot;&gt;with &lt;code&gt;#[repr(C)]&lt;/code&gt;&lt;/a&gt;. The crate &lt;a href=&quot;https://crates.io/crates/abi_stable&quot;&gt;&lt;code&gt;abi_stable&lt;/code&gt;&lt;/a&gt; exists for this exact purpose, with an equivalent for the most important types. Theoretically, the task should be as easy as changing the &lt;code&gt;std&lt;/code&gt; types in our core &lt;code&gt;enum&lt;/code&gt; with theirs:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Before (simplified)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Box&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Bytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// After (simplified)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;abi_stable&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;std_types&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RVec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RBox&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RHashMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token attribute attr-name&quot;&gt;#[repr(C)]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RBox&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RHashMap&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Bytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RVec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;Value&lt;/code&gt; type is used a lot in the codebase, so this breaking change brought up lots of compilation errors. But for whatever reason, 70 of these errors were related to lifetimes, which I hadn’t changed at all…&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_debugging&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;debugging&quot; tabindex=&quot;-1&quot;&gt;Debugging &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-variance/#debugging&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Instead of a &lt;code&gt;String&lt;/code&gt; or &lt;code&gt;Vec&lt;/code&gt;, we actually used &lt;a href=&quot;https://doc.rust-lang.org/stable/alloc/borrow/enum.Cow.html&quot;&gt;&lt;code&gt;Cow&amp;lt;&#39;a&amp;gt;&lt;/code&gt;&lt;/a&gt; for performance reasons. &lt;code&gt;Cow&amp;lt;&#39;a&amp;gt;&lt;/code&gt; is a type that can hold either a borrowed or an owned value at runtime. The idea is to use the borrowed one as much as possible, and only if ownership or mutation is needed, the underlying value is cloned (&lt;a href=&quot;https://www.reddit.com/r/rust/comments/v1z6bx/what_is_a_cow/iape1qq/&quot;&gt;a better explanation can be found here&lt;/a&gt;).&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://mastodon.social/@heinz&quot;&gt;Heinz&lt;/a&gt;, my mentor at Tremor, managed to reproduce the issue to &lt;code&gt;Cow&lt;/code&gt; and its equivalent, &lt;code&gt;RCow&lt;/code&gt;. &lt;a href=&quot;https://play.rust-lang.org/?version=stable&amp;amp;mode=debug&amp;amp;edition=2021&amp;amp;gist=660f8633738fd0a8817cc8ee9bbddfa8&quot;&gt;A Playground snippet can be found here&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;abi_stable&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;std_types&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RCow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;std&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;borrow&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Cow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// This works&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;cmp_cow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;b&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;left&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Cow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; right&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Cow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    left &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; right
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// This fails to compile&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;cmp_rcow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;b&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;left&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RCow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; right&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RCow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    left &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; right
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It failed to compile with the following error, which didn’t help much. In Rust 1.62.0, they actually improved it to explain what’s going on (shown at the end of the article):&lt;/p&gt;
&lt;pre class=&quot;language-plain&quot;&gt;&lt;code class=&quot;language-plain&quot;&gt;$ cargo b
   Compiling repro v0.1.0 (/home/mario/Downloads/repro)
error[E0623]: lifetime mismatch
  --&gt; src/lib.rs:10:10
   |
9  | fn cmp_rcow&amp;lt;&#39;a, &#39;b&gt;(left: &amp;amp;RCow&amp;lt;&#39;a, ()&gt;, right: &amp;amp;RCow&amp;lt;&#39;b, ()&gt;) -&gt; bool {
   |                            ------------          ------------
   |                            |
   |                            these two types are declared with different lifetimes...
10 |     left == right
   |          ^^ ...but data from `left` flows into `right` here

For more information about this error, try `rustc --explain E0623`.
error: could not compile `repro` due to previous error&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What? Why do lifetimes matter here if it’s just a comparison?&lt;/p&gt;
&lt;p&gt;This hinted that the issue was in the underlying library, not my code. &lt;code&gt;RCow&lt;/code&gt; is supposed to be a drop-in replacement for &lt;code&gt;Cow&lt;/code&gt;. It also had something to do with &lt;code&gt;PartialOrd&lt;/code&gt;, which is the trait used for &lt;code&gt;==&lt;/code&gt; here. But I couldn’t see a difference in its implementations:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Sized&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PartialOrd&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Cow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PartialOrd&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ToOwned&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute attr-name&quot;&gt;#[inline]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;partial_cmp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; other&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Cow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Ordering&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;PartialOrd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;partial_cmp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;other&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PartialOrd&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RCow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RCow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PartialOrd&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BorrowOwned&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Sized&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute attr-name&quot;&gt;#[inline]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;partial_cmp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; other&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RCow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Ordering&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;PartialOrd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;partial_cmp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;other&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are more libraries providing drop-in replacements for &lt;code&gt;Cow&lt;/code&gt;. And for example, &lt;a href=&quot;https://crates.io/crates/beef&quot;&gt;&lt;code&gt;beef&lt;/code&gt;&lt;/a&gt; managed to get it right, somehow. I wasn’t able to reproduce the issue with their version… but why?&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PartialOrd&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;beef&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Cow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;beef&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Cow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;A&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Beef&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Sized&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PartialOrd&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Beef&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Sized&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;U&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Capacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Capacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute attr-name&quot;&gt;#[inline]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;partial_cmp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; other&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;beef&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Cow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;V&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Ordering&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;PartialOrd&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;partial_cmp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;borrow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; other&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;borrow&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a name=&quot;_some_progress_or_not&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;some-progress-or-not&quot; tabindex=&quot;-1&quot;&gt;Some progress… or not? &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-variance/#some-progress-or-not&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Other traits like &lt;code&gt;PartialEq&lt;/code&gt; also caused similar lifetime errors. I was able to fix some by introducing a new lifetime &lt;code&gt;&#39;b&lt;/code&gt; into the trait implementation. This indicated the Rust compiler that comparing objects with different lifetimes is okay:&lt;/p&gt;
&lt;pre class=&quot;language-diff-rust&quot;&gt;&lt;code class=&quot;language-diff-rust&quot;&gt;&lt;span class=&quot;token deleted-sign deleted language-rust&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PartialEq&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RCow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RCow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted language-rust&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PartialEq&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RCow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RCow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token unchanged language-rust&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;    &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PartialEq&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BorrowOwned&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Sized&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted language-rust&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;    &lt;span class=&quot;token class-name&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BorrowOwned&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;b&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Sized&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token unchanged language-rust&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token deleted-sign deleted language-rust&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; other&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RCow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted language-rust&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; other&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RCow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;C&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token unchanged language-rust&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;        &lt;span class=&quot;token class-name&quot;&gt;PartialEq&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;eq&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;other&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I suddenly got a bit of hope. But this could never work for &lt;code&gt;Ord&lt;/code&gt;, which also failed. The &lt;code&gt;Ord&lt;/code&gt; trait uses &lt;code&gt;Self&lt;/code&gt; for the &lt;code&gt;other&lt;/code&gt; parameter, so I can’t just introduce a new lifetime.&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Sized&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Ord&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RCow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Ord&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BorrowOwned&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute attr-name&quot;&gt;#[inline]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;cmp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; other&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Ordering&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Ord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cmp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;other&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Implementation in the standard library:&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Sized&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Ord&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Cow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Ord&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ToOwned&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute attr-name&quot;&gt;#[inline]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;cmp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; other&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Ordering&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Ord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cmp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;other&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a name=&quot;_discovering_the_root_cause&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;discovering-the-root-cause&quot; tabindex=&quot;-1&quot;&gt;Discovering the root cause &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-variance/#discovering-the-root-cause&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Some wonderful people on the Rust Discord server helped me understand what was going on. So I started learning more about the so-called “Subtyping and Variance”.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/img/xEXV5m-EQx-1349.avif 1349w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/img/xEXV5m-EQx-1349.webp 1349w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://nullderef.com/img/xEXV5m-EQx-1349.png&quot; alt=&quot;Discord discussion&quot; width=&quot;1349&quot; height=&quot;368&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;This topic isn’t covered in &lt;a href=&quot;https://doc.rust-lang.org/book/&quot;&gt;The Rust Book&lt;/a&gt;. We’ll only find it in its more obscure, unsafer brother, The Rustonomicon. This book explains it incredibly well, so I won’t repeat it here. Here are some resources:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href=&quot;https://doc.rust-lang.org/nomicon/subtyping.html&quot;&gt;“Subtyping and Variance” — The Rustonomicon&lt;/a&gt; (&lt;em&gt;an explanation&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://doc.rust-lang.org/reference/subtyping.html&quot;&gt;“Subtyping and Variance” — The Rust Reference&lt;/a&gt; (&lt;em&gt;a cheatsheet&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Covariance_and_contravariance_%28computer_science%29&quot;&gt;“Covariance and contravariance” — Wikipedia&lt;/a&gt; (&lt;em&gt;the general term&lt;/em&gt;)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A couple blog posts take a more practical approach, like &lt;a href=&quot;https://medium.com/@orbitalK/rust-lifetime-subtype-variance-b58434fe36ed&quot;&gt;“Rust Lifetime Subtype Variance” — Prolific K&lt;/a&gt; or &lt;a href=&quot;https://lcnr.de/blog/diving-deep-implied-bounds-and-variance/&quot;&gt;“Diving Deep: implied bounds and variance” — lcnr.de&lt;/a&gt;. Or if you’re a visual learner, &lt;a href=&quot;https://www.youtube.com/watch?v=iVYWDIW71jk&quot;&gt;this video from Jon Gjengset&lt;/a&gt; might be best for you.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_trying_to_fix_it&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;trying-to-fix-it&quot; tabindex=&quot;-1&quot;&gt;Trying to fix it &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-variance/#trying-to-fix-it&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The difference between &lt;code&gt;RCow&lt;/code&gt; and &lt;code&gt;Cow&lt;/code&gt; was the &lt;code&gt;BorrowOwned&amp;lt;&#39;a&amp;gt;&lt;/code&gt; trait. For technical reasons, it was being used as a &lt;a href=&quot;https://doc.rust-lang.org/rust-by-example/trait/supertraits.html&quot;&gt;subtrait&lt;/a&gt; of &lt;code&gt;ToOwned&lt;/code&gt;, and it had to bind to a lifetime &lt;code&gt;&#39;a&lt;/code&gt;. Ultimately, this made &lt;code&gt;RCow&lt;/code&gt; &lt;em&gt;invariant&lt;/em&gt; over &lt;code&gt;&#39;a&lt;/code&gt;, while &lt;code&gt;Cow&lt;/code&gt; was &lt;em&gt;covariant&lt;/em&gt;. We want &lt;code&gt;RCow&lt;/code&gt; to be &lt;em&gt;covariant&lt;/em&gt; for this to work.&lt;/p&gt;
&lt;pre class=&quot;language-diff-rust&quot;&gt;&lt;code class=&quot;language-diff-rust&quot;&gt;&lt;span class=&quot;token unchanged language-rust&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Sized&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Ord&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Cow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token deleted-sign deleted language-rust&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;    &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Ord&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ToOwned&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// in Cow&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted language-rust&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;    &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Ord&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BorrowOwned&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// in RCow&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token unchanged language-rust&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;    &lt;span class=&quot;token attribute attr-name&quot;&gt;#[inline]&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;cmp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; other&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Ordering&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;        &lt;span class=&quot;token class-name&quot;&gt;Ord&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cmp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;other&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a name=&quot;_attempt_1_gats&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;attempt-1-gats&quot; tabindex=&quot;-1&quot;&gt;Attempt #1: GATs &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-variance/#attempt-1-gats&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I had an idea of using &lt;a href=&quot;https://blog.rust-lang.org/2022/10/28/gats-stabilization.html&quot;&gt;Generic Associated Types (GATs)&lt;/a&gt;. Instead of binding the lifetime to the trait, I could do so to its associated type. Then, I’d be able to use &lt;code&gt;BorrowOwned&lt;/code&gt; instead of &lt;code&gt;BorrowOwned&amp;lt;&#39;a&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BorrowOwned&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;RBorrowed&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But &lt;a href=&quot;https://rustc-dev-guide.rust-lang.org/variance.html#variance-and-associated-types&quot;&gt;a section in the Rust Developer Book&lt;/a&gt; states that “traits with associated types must be invariant with respect to all of their inputs”. So that still didn’t help make our type covariant.&lt;/p&gt;
&lt;p&gt;Note I only found that statement in the book for developers of the compiler! I opened &lt;a href=&quot;https://github.com/rust-lang/nomicon/issues/338&quot;&gt;an issue about that in The Rustonomicon&lt;/a&gt;, and moved on to something else.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_attempt_2_transmute&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;attempt-2-transmute&quot; tabindex=&quot;-1&quot;&gt;Attempt #2: &lt;code&gt;transmute&lt;/code&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-variance/#attempt-2-transmute&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;After many wasted hours, I was tempted to use &lt;code&gt;transmute&lt;/code&gt; and call it a day. Here’s what Heinz suggested (&lt;em&gt;trigger warning&lt;/em&gt;):&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;compare&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;b&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;left&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RCow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; right&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RCow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Ordering&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;unsafe&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; right&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RCow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;std&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;mem&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;transmute&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;right&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        left&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;cmp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;right&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It worked! In theory, it’s safe because both &lt;code&gt;&#39;a&lt;/code&gt; and &lt;code&gt;&#39;b&lt;/code&gt; will live for at least as long as the function does, and we’re returning an owned type.&lt;/p&gt;
&lt;p&gt;Ideally, we’d abstract it away by writing a wrapper around &lt;code&gt;RCow&lt;/code&gt; with the fix. But that wouldn’t help because invariant relationships are inherited, and the wrapper’s implementation of &lt;code&gt;Ord&lt;/code&gt; would still use &lt;code&gt;BorrowOwned&amp;lt;&#39;a&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;SCow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RCow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// will still be invariant!&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;One workaround would be to hide &lt;code&gt;RCow&lt;/code&gt; under a &lt;code&gt;*const ()&lt;/code&gt;. Then, I can pointer-cast back and forth from it. But in this project, I already had too many things backfire. Traumatized, I continued looking for a solution.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_attempt_3_getting_rid_of_borrowowneda&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;attempt-3-getting-rid-of-borrowowned-a&quot; tabindex=&quot;-1&quot;&gt;Attempt #3: getting rid of &lt;code&gt;BorrowOwned&amp;lt;&#39;a&amp;gt;&lt;/code&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-variance/#attempt-3-getting-rid-of-borrowowned-a&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The best way to not have problems with this trait is to get rid of it. The standard library has &lt;code&gt;ToOwned&lt;/code&gt;, which links a borrowed type with its owned counterpart. For example, &lt;code&gt;&amp;amp;str&lt;/code&gt; and &lt;code&gt;String&lt;/code&gt;. If &lt;code&gt;Cow&amp;lt;B&amp;gt;&lt;/code&gt; requires &lt;code&gt;B: ToOwned&lt;/code&gt;, then the &lt;code&gt;Cow::Borrowed&lt;/code&gt; variant can just hold &lt;code&gt;&amp;amp;B&lt;/code&gt; and &lt;code&gt;Cow::Owned&lt;/code&gt; can hold &lt;code&gt;B::Owned&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;BorrowOwned&amp;lt;&#39;a&amp;gt;&lt;/code&gt; roughly did the same thing for types defined in &lt;code&gt;abi_stable&lt;/code&gt;, such as &lt;code&gt;RStr&lt;/code&gt; and &lt;code&gt;RString&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// standard library&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;abc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; x_owned&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to_owned&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// abi_stable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; x_ffi_safe&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RStr&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;_&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token macro property&quot;&gt;rstr!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;abc&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; x_owned&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to_owned&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; x_ffi_safe_owned&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RString&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;r_to_owned&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that we need a lifetime in &lt;code&gt;BorrowOwned&lt;/code&gt; because the equivalent of &lt;code&gt;&amp;amp;&#39;a str&lt;/code&gt; is &lt;code&gt;RStr&amp;lt;&#39;a&amp;gt;&lt;/code&gt;. Which is not exactly the same. This is because &lt;code&gt;str&lt;/code&gt; is a &lt;a href=&quot;https://doc.rust-lang.org/nomicon/exotic-sizes.html#dynamically-sized-types-dsts&quot;&gt;Dynamically Sized Type (DST)&lt;/a&gt;, but custom DSTs aren’t supported by Rust.&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ToOwned&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;  &lt;span class=&quot;token comment&quot;&gt;// okay&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Owned&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// `&amp;amp;self` is `&amp;amp;str`&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;to_owned&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ToOwned&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RStr&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Owned&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// `&amp;amp;self` is `&amp;amp;RStr&amp;lt;&#39;a&gt;`, but we want `RStr&amp;lt;&#39;a&gt;`&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// So we can&#39;t quite use `ToOwned` here&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;to_owned&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RString&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So instead of establishing this relationship through a trait, we can introduce a new generic paramter &lt;code&gt;O&lt;/code&gt;. &lt;code&gt;B&lt;/code&gt; would be the borrowed type, and &lt;code&gt;O&lt;/code&gt; the owned one. This is similar to what the &lt;a href=&quot;https://crates.io/crates/cervine&quot;&gt;&lt;code&gt;cervine&lt;/code&gt;&lt;/a&gt; crate does, which relaxes the constraints of &lt;code&gt;Cow&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Before:&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#[repr(C)]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;RCow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BorrowOwned&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Sized&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Borrowed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BorrowOwned&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RBorrowed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Owned&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BorrowOwned&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ROwned&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// After:&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#[repr(C)]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;RCow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;O&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Borrowed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;B&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Owned&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;O&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/// Ffi-safe equivalent of `Cow&amp;lt;&#39;a, T&gt;`, either a `&amp;amp;T` or `T`.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;RCowVal&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RCow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/// Ffi-safe equivalent of `Cow&amp;lt;&#39;a, str&gt;`, either an `RStr` or `RString`.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;RCowStr&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RCow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RStr&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RString&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/// Ffi-safe equivalent of `Cow&amp;lt;&#39;a, [T]&gt;`, either an `RSlice` or `RVec`.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;RCowSlice&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RCow&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RSlice&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RVec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Without the &lt;code&gt;BorrowOwned&lt;/code&gt; trait, our struct was now covariant over &lt;code&gt;&#39;a&lt;/code&gt;, and the errors disappeared. &lt;a href=&quot;https://github.com/rodrimati1992&quot;&gt;Rodri&lt;/a&gt;, the author of &lt;code&gt;abi_stable&lt;/code&gt; ended up proposing &lt;a href=&quot;https://github.com/rodrimati1992/abi_stable_crates/commit/0b048ecf07177d1aa664a65d3a78fe5a2aba421e&quot;&gt;the fix that was merged&lt;/a&gt;. You can find &lt;a href=&quot;https://github.com/rodrimati1992/abi_stable_crates/issues/75#issuecomment-1043874752&quot;&gt;a simplified version here&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_conclusion&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-variance/#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This showcased two gaps in the language:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;There were no indications in the error message about the issue being related to “variance”. I had no idea what that was, and it wasn’t covered in the book.&lt;/li&gt;
&lt;li&gt;It was very hard to debug the variance of a type, given that they are implicit.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;So it’s amazing to hear that starting in Rust 1.62.0, you’re even taken to the documentation. It will still be hard to understand the whole topic, but at least you know where to start!&lt;/p&gt;
&lt;pre class=&quot;language-plain&quot;&gt;&lt;code class=&quot;language-plain&quot;&gt;error: lifetime may not live long enough
  --&gt; src/main.rs:55:5
   |
54 | fn test2&amp;lt;&#39;a, &#39;b&gt;(left: &amp;amp;RCow&amp;lt;&#39;a, u8&gt;, right: &amp;amp;RCow&amp;lt;&#39;b, u8&gt;) -&gt; Ordering {
   |          --  -- lifetime `&#39;b` defined here
   |          |
   |          lifetime `&#39;a` defined here
55 |     left.cmp(right)
   |     ^^^^^^^^^^^^^^^ argument requires that `&#39;a` must outlive `&#39;b`
   |
   = help: consider adding the following bound: `&#39;a: &#39;b`
   = note: requirement occurs because of the type `RCow&amp;lt;&#39;_, u8&gt;`, which makes the generic argument `&#39;_` invariant
   = note: the enum `RCow&amp;lt;&#39;a, B&gt;` is invariant over the parameter `&#39;a`
   = help: see &amp;lt;https://doc.rust-lang.org/nomicon/subtyping.html&gt; for more information about variance&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I was lucky to have such a great team at Tremor, and an OSS maintainer as helpful as Rodri. You can find all the details of the discussion in the original GitHub issue:&lt;/p&gt;
&lt;p style=&quot;text-align:center&quot;&gt;
  &lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&amp;nbsp;&lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rodrimati1992/abi_stable_crates/issues/75&quot;&gt;lifetimes with R* types break compared to non R* types &lt;span class=&quot;gh-number&quot;&gt;rodrimati1992/abi_stable_crates#75&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;
&lt;/p&gt;

&lt;script src=&quot;https://f.convertkit.com/ckjs/ck.5.js&quot;&gt;&lt;/script&gt;
&lt;form action=&quot;https://app.kit.com/forms/7534343/subscriptions&quot; class=&quot;seva-form formkit-form&quot; method=&quot;post&quot; data-sv-form=&quot;7534343&quot; data-uid=&quot;3680381778&quot; data-format=&quot;inline&quot; data-version=&quot;5&quot; data-options=&#39;{&quot;settings&quot;:{&quot;after_subscribe&quot;:{&quot;action&quot;:&quot;message&quot;,&quot;success_message&quot;:&quot;Success! Check your email to confirm your subscription. If you can’t find it, look in the spam folder.&quot;,&quot;redirect_url&quot;:&quot;&quot;},&quot;analytics&quot;:{&quot;google&quot;:null,&quot;fathom&quot;:null,&quot;facebook&quot;:null,&quot;segment&quot;:null,&quot;pinterest&quot;:null,&quot;sparkloop&quot;:null,&quot;googletagmanager&quot;:null},&quot;modal&quot;:{&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;powered_by&quot;:{&quot;show&quot;:true,&quot;url&quot;:&quot;https://kit.com/features/forms?utm_campaign=poweredby&amp;amp;utm_content=form&amp;amp;utm_medium=referral&amp;amp;utm_source=dynamic&quot;},&quot;recaptcha&quot;:{&quot;enabled&quot;:false},&quot;return_visitor&quot;:{&quot;action&quot;:&quot;show&quot;,&quot;custom_content&quot;:&quot;&quot;},&quot;slide_in&quot;:{&quot;display_in&quot;:&quot;bottom_right&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;sticky_bar&quot;:{&quot;display_in&quot;:&quot;top&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15}},&quot;version&quot;:&quot;5&quot;}&#39; min-width=&quot;400 500 600 700 800&quot;&gt;
  &lt;div data-style=&quot;clean&quot;&gt;
    &lt;div class=&quot;formkit-header&quot; data-element=&quot;header&quot;&gt;
      &lt;h2&gt;Want to receive an email when I post?&lt;/h2&gt;
    &lt;/div&gt;
    &lt;ul class=&quot;formkit-alert formkit-alert-error&quot; data-element=&quot;errors&quot; data-group=&quot;alert&quot;&gt;&lt;/ul&gt;
    &lt;div data-element=&quot;fields&quot; data-stacked=&quot;false&quot; class=&quot;seva-fields formkit-fields&quot;&gt;
      &lt;div class=&quot;formkit-field&quot;&gt;
        &lt;input class=&quot;formkit-input&quot; name=&quot;email_address&quot; aria-label=&quot;Email Address&quot; placeholder=&quot;Email Address&quot; required=&quot;&quot; type=&quot;email&quot;&gt;
      &lt;/div&gt;
      &lt;button data-element=&quot;submit&quot; class=&quot;formkit-submit&quot; style=&quot;background-color:#e06c75;font-weight:700&quot;&gt;
        &lt;div class=&quot;formkit-spinner&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span class=&quot;&quot;&gt;Subscribe&lt;/span&gt;
      &lt;/button&gt;
    &lt;/div&gt;
    &lt;div class=&quot;formkit-guarantee&quot; data-element=&quot;guarantee&quot;&gt;
      &lt;p&gt;&lt;em&gt;Free and without spam. You can also use my &lt;a href=&quot;https://nullderef.com/index.xml&quot; target=&quot;_blank&quot;&gt;RSS&lt;/a&gt;, &lt;a href=&quot;https://x.com/null_deref&quot; target=&quot;_blank&quot;&gt;X&lt;/a&gt;, or &lt;a href=&quot;https://mstdn.social/@nullderef&quot; target=&quot;_blank&quot;&gt;Mastodon&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
      &lt;p style=&quot;opacity:.6;font-size:16px&quot;&gt;&lt;em&gt;Powered by &lt;a href=&quot;https://kit.com/&quot; target=&quot;_black&quot;&gt;Kit&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;style&gt;.formkit-form[data-uid=&quot;3680381778&quot;] a{text-decoration:underline}.formkit-form[data-uid=&quot;3680381778&quot;] legend{border:none;font-size:inherit;margin-bottom:10px;padding:0;position:relative;display:table}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-checkboxes,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-select{width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{border:0;border-radius:var(--radius);color:#fff;cursor:pointer;display:inline-block;text-align:center;font-weight:500;cursor:pointer;margin-bottom:15px;overflow:hidden;padding:0;position:relative;vertical-align:middle}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover{outline:0}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover&gt;span{background-color:rgba(0,0,0,.1)}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit&gt;span{display:block;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out;padding:12px 24px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input{background:#fff;padding:12px;border:1px solid var(--border);border-radius:var(--radius);-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;line-height:1.4;margin:0;-webkit-transition:border-color ease-out .3s;transition:border-color ease-out .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:focus{outline:0;border-color:#1677be;-webkit-transition:border-color ease .3s;transition:border-color ease .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-webkit-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-moz-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:-ms-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert{background:#f9fafb;border:1px solid #e3e3e3;border-radius:5px;-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;list-style:none;margin:25px auto;padding:12px;text-align:center;width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert:empty{display:none}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-success{background:#d3fbeb;border-color:#10bf7a;color:#0c905c}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-error{background:#fde8e2;border-color:#f2643b;color:#ea4110}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;height:0;width:0;margin:0 auto;position:absolute;top:0;left:0;right:0;width:0;overflow:hidden;text-align:center;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div{margin:auto;width:12px;height:12px;background-color:#fff;opacity:.3;border-radius:100%;display:inline-block;-webkit-animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both;animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:first-child{-webkit-animation-delay:-.32s;animation-delay:-.32s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:nth-child(2){-webkit-animation-delay:-.16s;animation-delay:-.16s}.formkit-form[data-uid=&quot;3680381778&quot;]{border:1px solid var(--border);border-radius:var(--radius);position:relative;overflow:hidden;background:var(--code-bg)}.formkit-form[data-uid=&quot;3680381778&quot;]{padding:20px;width:100%;position:relative}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-header{margin:0 0 27px 0;font-size:20px;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee{margin-top:10px;margin-bottom:32px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee&gt;p{margin:0;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-fields{display:flex;flex-wrap:wrap;margin:25px auto 0 auto;gap:10px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-field,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{flex:1 0 100%}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;]{padding:0 30px}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-field{flex:100 1 auto}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-submit{flex:1 1 auto}&lt;/style&gt;
&lt;/form&gt;
</content>
  </entry>
  <entry>
    <title>Beyond Dumbphones: Building a Minimalist Yet Functional Phone</title>
    <link href="https://nullderef.com/blog/phone-intro/" />
    <updated>2024-06-01T00:00:00Z</updated>
    <id>https://nullderef.com/blog/phone-intro/</id>
    <content type="html">&lt;details class=&quot;toc&quot;&gt;
&lt;summary&gt;Table of Contents&lt;/summary&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#looking-at-the-state-of-things&quot;&gt;Looking at the state of things &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#technology&quot;&gt;Technology &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#negative-effects&quot;&gt;Negative effects &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#companies&quot;&gt;Companies &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#software&quot;&gt;Software &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#accessories&quot;&gt;Accessories &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#phones&quot;&gt;Phones &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#wrapping-up&quot;&gt;Wrapping up &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/details&gt;
&lt;p&gt;What if there was a company selling phones that, against all odds, optimized how &lt;em&gt;little&lt;/em&gt; they were used? There’s a lot of potential here, given that every company is trying to get you hooked on their app.&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Feature_phone&quot;&gt;“Dumbphones”&lt;/a&gt; have emerged for this very reason. These are phones whose functionality is intentionally locked down, with the purest examples only supporting calls and SMS. On the other side of the spectrum, we have smartphones. It wouldn’t be far-fetched to say that most people owning one use it more than they’d like.&lt;/p&gt;
&lt;p&gt;I’ve seen people around me switching to a dumbphone. A &lt;em&gt;digital detox&lt;/em&gt; is not a bad idea; it helps reduce anxiety in the short term. But in the longer term, it’s pretty much impossible. A few apps like WhatsApp or Google Maps can be indispensable, and the rest actually improve our lifes when used reasonably. TikTok can be incredibly addicting, but it’s arguably a great way for people to learn, relax, or connect with others.&lt;/p&gt;
&lt;p&gt;Even the founders of the &lt;a href=&quot;https://en.wikipedia.org/wiki/Light_Phone&quot;&gt;Light Phone&lt;/a&gt; — the most popular dumbphone brand — thought it was too restrictive for them, and they have released a more featureful version&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;. Don’t get me wrong! I admire the company and all the work they do; this is such a small industry that anything helps. But I wonder if there’s something that could become more mainstream.&lt;/p&gt;
&lt;p&gt;Besides, as a big fan of second-hand stuff, I’d rather give an old Motorola a second life than bring yet another phone to the world. It’d also be really cool to open and close a retro flip-phone (right???).&lt;/p&gt;
&lt;p&gt;So what if we tried to find a middle point? There are two possible approaches:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;A dumbphone trying to be a smartphone&lt;/li&gt;
&lt;li&gt;Or a smartphone trying to be a dumbphone&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;I’m a bigger fan of the latter, because most dumbphones don’t give you direct access to an app store. Given how huge the mobile ecosystem is, supporting everyone’s use-cases one by one ends up being very restrictive. Your dumbphone’s operating system could add custom support for Uber, but there may be someone who won’t buy the phone unless it has Spotify. And a password manager. And their banking app. And after implementing all of these, someone will need a damned parking app for their apartment complex (yes, really&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;).&lt;/p&gt;
&lt;p&gt;The question arises: how in the world would you even design a phone? It sounds like the most daunting task, so that’s what I’ll be researching in this series! We’ll get started with broader topics like addiction, building companies, or existing solutions, and eventually get into more details. I’m no expert, so let’s see how far the internet can take us.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_looking_at_the_state_of_things&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;looking-at-the-state-of-things&quot; tabindex=&quot;-1&quot;&gt;Looking at the state of things &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/phone-intro/#looking-at-the-state-of-things&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’ve been &lt;a href=&quot;https://xkcd.com/356/&quot;&gt;&lt;s&gt;nerdsniped&lt;/s&gt;&lt;/a&gt; drawn to this space for some time, learning more about it from different perspectives:&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_technology&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;technology&quot; tabindex=&quot;-1&quot;&gt;Technology &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/phone-intro/#technology&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Firstly, let’s take a step back from “phones that shouldn’t be used”. Can we generalize it to “tech that shouldn’t be used”? Or rather, “tech that doesn’t unnecessarily grab your attention”? Now we’re onto something! That’s what &lt;a href=&quot;https://en.wikipedia.org/wiki/Calm_technology&quot;&gt;“Calm Technology”&lt;/a&gt; is all about, a concept coined by Mark Weiser and John Seely Brown at &lt;a href=&quot;https://en.wikipedia.org/wiki/PARC_(company)&quot;&gt;XEROX Parc&lt;/a&gt; in the &#39;90s.&lt;/p&gt;
&lt;p&gt;Calm Technology describes design principles that let a piece of technology &lt;em&gt;inform&lt;/em&gt; the user without &lt;em&gt;demanding&lt;/em&gt; their attention. Anything from hammers to Roombas can be considered “calm”. Researcher Amber Case &lt;a href=&quot;https://calmtech.com/&quot;&gt;picked up on this&lt;/a&gt; and recently announced &lt;a href=&quot;https://www.calmtech.institute/&quot;&gt;an institute&lt;/a&gt; to certify products that meet these standards. Also inspired by Mark and John’s original work, Jon Yablonski shares his take and more resources in his website &lt;a href=&quot;https://humanebydesign.com/&quot;&gt;Humane By Design&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;The &lt;a href=&quot;https://www.humanetech.com/&quot;&gt;Center for Humane Technology&lt;/a&gt; is a more established organization on this topic. This non-profit aims to educate people and companies, raise awareness, and aid in policymaking. It gained popularity in Neflix’s hit documentary &lt;a href=&quot;https://www.thesocialdilemma.com/&quot;&gt;&lt;em&gt;The Social Dilemma&lt;/em&gt;&lt;/a&gt;, which focuses on social media. One of its founders, Tristan Harris, created &lt;a href=&quot;http://www.minimizedistraction.com/&quot;&gt;an internal presentation at Google&lt;/a&gt; in 2013 that has aged like fine wine.&lt;/p&gt;
&lt;p&gt;On the flip side, Cal Newport writes about what &lt;em&gt;humans&lt;/em&gt; can do to stop their attention from being hijacked. He calls it &lt;a href=&quot;https://www.goodreads.com/book/show/40672036-digital-minimalism&quot;&gt;“Digital Minimalism”&lt;/a&gt; and also covers managing your attention span in &lt;a href=&quot;https://www.goodreads.com/book/show/25744928-deep-work&quot;&gt;“Deep Work”&lt;/a&gt;, or living a more minimalist life in general. This is also discussed on &lt;a href=&quot;https://nosurf.net/&quot;&gt;NoSurf&lt;/a&gt;, the biggest community on the topic I could find, with even more information&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_negative_effects&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;negative-effects&quot; tabindex=&quot;-1&quot;&gt;Negative effects &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/phone-intro/#negative-effects&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Is the population aware of &lt;a href=&quot;https://en.wikipedia.org/wiki/Problematic_smartphone_use&quot;&gt;problematic smartphone use&lt;/a&gt;? Is it even a problem to them? Certainly, there are studies on how phone usage leads to poorer psychological health. But it doesn’t help that there’s no medical consensus nor recognition; &lt;a href=&quot;https://youtu.be/8B271L3NtAw?t=10&quot;&gt;there’s correlation, but not clear causation&lt;/a&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn4&quot; id=&quot;fnref4&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt;&lt;/p&gt;
&lt;p&gt;Phones have undoubtedly impacted our society. Some changes are good, others, not so much. We fiddle with our phones to avoid eye contact, social media worsens our self-esteem, and we have attention issues. It’s just hard to be bored anymore, which is actually good for you&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn5&quot; id=&quot;fnref5&quot;&gt;[5]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn6&quot; id=&quot;fnref6&quot;&gt;[6]&lt;/a&gt;&lt;/sup&gt;. Phones also disrupt our sleep cycles&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn7&quot; id=&quot;fnref7&quot;&gt;[7]&lt;/a&gt;&lt;/sup&gt;, and cars frequently crash because of them&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn8&quot; id=&quot;fnref8&quot;&gt;[8]&lt;/a&gt;&lt;/sup&gt;. Nothing you don’t already know.&lt;/p&gt;
&lt;p&gt;There are increasingly more stories about &lt;a href=&quot;https://en.wikipedia.org/wiki/Hikikomori&quot;&gt;Hikikomori&lt;/a&gt;, described as “total withdrawal from society and seeking extreme degrees of social isolation and confinement”. Of course, it’s not limited to Japan. Wikipedia sheds light on the role of technology in this condition:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Although the connection between modern communication technologies (such as the Internet, social media and video games) and the phenomenon is not conclusively established, those technologies are considered at least an exacerbating factor that can deepen and nurture withdrawal.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It even specifically mentions our topic:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;The emergence of mobile phones and then smartphones may also have deepened the issue, given that people can continue their addiction to gaming and online surfing anywhere, even in bed.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Some people suffering from migraines or light sensitivity are affected by the screen itself&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn9&quot; id=&quot;fnref9&quot;&gt;[9]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn10&quot; id=&quot;fnref10&quot;&gt;[10]&lt;/a&gt;&lt;/sup&gt;. There’s also the topic of phones for children or the elderly, but that’s a huge rabbit hole. Looking at the broader population, nearly 6 in 10 U.S. smartphone users admit that they use their phone too much&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn11&quot; id=&quot;fnref11&quot;&gt;[11]&lt;/a&gt;&lt;/sup&gt;. So the good news is that people are at least aware of the issue. That surely means there’s a need for solutions, regardless of science?&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_companies&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;companies&quot; tabindex=&quot;-1&quot;&gt;Companies &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/phone-intro/#companies&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In order to start a company that cares about people’s mental health, it’s sensible to say that its workers shouldn’t be crunching 60 hours per week. I learned a lot about this by reading &lt;a href=&quot;https://basecamp.com/books/calm&quot;&gt;“It doesn’t have to be crazy at work”&lt;/a&gt;, written by the founders of Basecamp. Part of its contents are also available online&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn12&quot; id=&quot;fnref12&quot;&gt;[12]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn13&quot; id=&quot;fnref13&quot;&gt;[13]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn14&quot; id=&quot;fnref14&quot;&gt;[14]&lt;/a&gt;&lt;/sup&gt;. You don’t have to agree with everything to admit it makes you rethink how companies are built.&lt;/p&gt;
&lt;p&gt;This includes leadership, benefits, planning, hiring, funding… and how to make them more reasonable. One key idea is that starting a business with the goal of changing the world sets the stakes too high from the beginning. The desire to get rich becomes the justification for all-nighters and bad practices, which spreads from founders to employees. Other so-called “Calm Companies” that share this mindset include &lt;a href=&quot;http://Transistor.fm&quot;&gt;Transistor.fm&lt;/a&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn15&quot; id=&quot;fnref15&quot;&gt;[15]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn16&quot; id=&quot;fnref16&quot;&gt;[16]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn17&quot; id=&quot;fnref17&quot;&gt;[17]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn18&quot; id=&quot;fnref18&quot;&gt;[18]&lt;/a&gt;&lt;/sup&gt;, Wildbit&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn19&quot; id=&quot;fnref19&quot;&gt;[19]&lt;/a&gt;&lt;/sup&gt;, or Convertkit&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn20&quot; id=&quot;fnref20&quot;&gt;[20]&lt;/a&gt;&lt;/sup&gt;, but Basecamp is the loudest about it.&lt;/p&gt;
&lt;p&gt;An important caveat is that we must ensure the company stays true to its values. In this case, that the less screen time, the better (within profitable means). Not that the more revenue, the better. However, this depends on who controls the company, which is often the person who provides the funding.&lt;/p&gt;
&lt;p&gt;We can learn this lesson from &lt;a href=&quot;https://en.wikipedia.org/wiki/CouchSurfing&quot;&gt;CouchSurfing&lt;/a&gt;, a website that helps you find a couch at someone’s home for a short stay. The company’s goal wasn’t just to “connect hosts to guests”, but to “create lasting positive experiences and relationships between strangers”. And it wasn’t gibberish: their internal metric for success was “positive time had thanks to CouchSurfing” subtracted by “time spent on the website”&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn21&quot; id=&quot;fnref21&quot;&gt;[21]&lt;/a&gt;&lt;/sup&gt;. But after a hectic story, they changed to a for-profit and started heading in the opposite direction, fueled by the need for monetization&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn22&quot; id=&quot;fnref22&quot;&gt;[22]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;What most Calm Companies do is &lt;em&gt;bootstrap&lt;/em&gt;, meaning the founders use their own money. They promise never to go public, and Basecamp goes a step further by assuring they will never sell the company&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn23&quot; id=&quot;fnref23&quot;&gt;[23]&lt;/a&gt;&lt;/sup&gt;. Unfortunately, that only works for purely software companies; manufacturing hardware means you will need money. The &lt;a href=&quot;https://intl.nothing.tech/&quot;&gt;Nothing company&lt;/a&gt; spends $30 to $50 million for each new product they develop&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn24&quot; id=&quot;fnref24&quot;&gt;[24]&lt;/a&gt;&lt;/sup&gt;. On a “smaller” scale, the Light Phone 2 raised a total of $11.9 million in 2019&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn25&quot; id=&quot;fnref25&quot;&gt;[25]&lt;/a&gt;&lt;/sup&gt;. You’d probably do fine with much less, but it shows how hard it’d be to save up that kind of money yourself.&lt;/p&gt;
&lt;p&gt;A popular choice for niche hardware is &lt;a href=&quot;https://en.wikipedia.org/wiki/Crowdfunding&quot;&gt;crowdfunding&lt;/a&gt;, where the money comes from a bunch of interested people on the internet. Bigme, reMarkable, Light Phone, Minimal Phone, Librem. They all started like this. But from what I’ve learned, the crowdfunded money usually only covers the hardware costs, not so much the upfront design and testing. The successful path seems to be &lt;a href=&quot;https://en.wikipedia.org/wiki/Venture_capital&quot;&gt;Venture Capital (VC) funding&lt;/a&gt; to set up the team and build a prototype, and then crowdfunding to actually manufacture it.&lt;/p&gt;
&lt;p&gt;Going back to the Light Phone 2, they raised $3.5M from consumers on IndieGoGo&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn26&quot; id=&quot;fnref26&quot;&gt;[26]&lt;/a&gt;&lt;/sup&gt; but also $8.4M in seed funding&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn25&quot; id=&quot;fnref25:1&quot;&gt;[25:1]&lt;/a&gt;&lt;/sup&gt;. Similarly, reMarkable was able to secure $11M from presales but required an essential $10M in seed funding&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn27&quot; id=&quot;fnref27&quot;&gt;[27]&lt;/a&gt;&lt;/sup&gt;. It’s hard to avoid VC funding if you aren’t already a billionaire, like in the case of &lt;a href=&quot;https://mudita.com/&quot;&gt;Mudita&lt;/a&gt;. This company was founded by Michał Kiciński, who had already succeeded with &lt;a href=&quot;https://en.wikipedia.org/wiki/CD_Projekt&quot;&gt;CD Projekt&lt;/a&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn28&quot; id=&quot;fnref28&quot;&gt;[28]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Nowadays, startups raise VC money &lt;em&gt;precisely&lt;/em&gt; by asserting they will change the world. Their solution will blow the competition out of the water. Prepare to invest RIGHT NOW or miss the train. It’s just &lt;a href=&quot;https://en.wikipedia.org/wiki/Fear_of_missing_out&quot;&gt;FOMO&lt;/a&gt; for investors. How could this work in the context of a Calm Company?&lt;/p&gt;
&lt;p&gt;Although harder, I don’t believe it’s impossible. There are funds that leave plenty of freedom to the founders. And a minority of them are specialized in Calm Companies, such as CalmFund (which just &lt;a href=&quot;https://calmfund.com/writing/pause&quot;&gt;paused operations&lt;/a&gt;), &lt;a href=&quot;https://www.indie.vc/&quot;&gt;indie.vc&lt;/a&gt;, or &lt;a href=&quot;https://tinyseed.com/&quot;&gt;tinyseed&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Once you get over the necessity of raising money, there are other ways to preserve the company’s ideals. OpenAI famously failed to do so as a pure non-profit, allegedly because raising money was too hard as just a nonprofit&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn29&quot; id=&quot;fnref29&quot;&gt;[29]&lt;/a&gt;&lt;/sup&gt;. So maybe we could take that as a learned lesson. Other ideas are certifications like &lt;a href=&quot;https://en.wikipedia.org/wiki/B_Corporation_(certification)&quot;&gt;B Corporation&lt;/a&gt; or &lt;a href=&quot;https://en.wikipedia.org/wiki/Social_enterprise&quot;&gt;Social Enterprise&lt;/a&gt;, but I’m not sure how effective they are.&lt;/p&gt;
&lt;p&gt;You can see how much more I can research about this topic in future posts. And we haven’t even gotten to the phones section yet!&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_software&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;software&quot; tabindex=&quot;-1&quot;&gt;Software &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/phone-intro/#software&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Smartphones actually come with solid features to block apps and minimize screen time in general. However, they don’t seem to be good enough to gain adoption.&lt;/p&gt;
&lt;p&gt;Firstly, they aren’t well marketed; most people aren’t aware of their existence. Android calls the features &lt;a href=&quot;https://www.android.com/digital-wellbeing/&quot;&gt;“Digital Wellbeing”&lt;/a&gt; and iOS &lt;a href=&quot;https://support.apple.com/guide/iphone/get-started-with-screen-time-iphbfa595995/ios&quot;&gt;“Screen Time”&lt;/a&gt;. Personally, I receive more system notifications about new AI features in my camera than things like this.&lt;/p&gt;
&lt;p&gt;Secondly, they aren’t first-party citizens; the tools are there, but they don’t quite integrate seamlessly. The most powerful feature on Android is “modes”, which allows you to switch settings for different situations. For example: when your GPS shows you’re in the library, it can disable Instagram and set the screen to grayscale. Being so powerful, it’s also complicated to configure (and to keep your setup up to date). If the company prioritized reducing screen time over ad revenue, we’d likely see more ideas to improve its adoption.&lt;/p&gt;
&lt;p&gt;There are heaps of alternatives on the app marketplaces, although their source code may not be available, and most have in-app payments or ads. Here are some cool features I’ve seen while trying out Android apps&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn30&quot; id=&quot;fnref30&quot;&gt;[30]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn31&quot; id=&quot;fnref31&quot;&gt;[31]&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;fancy tutorials,&lt;/li&gt;
&lt;li&gt;syncing across devices (including your laptop or tablet),&lt;/li&gt;
&lt;li&gt;blocking websites (or even features inside an app, like YouTube Shorts),&lt;/li&gt;
&lt;li&gt;blocking pre-bundled categories of apps and websites (such as “shopping”),&lt;/li&gt;
&lt;li&gt;breathing exercises before opening apps (or having to read a book),&lt;/li&gt;
&lt;li&gt;motivational quotes,&lt;/li&gt;
&lt;li&gt;forums,&lt;/li&gt;
&lt;li&gt;a floating timer indicating total usage on that day,&lt;/li&gt;
&lt;li&gt;notification filtering and bundling,&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Gamification&quot;&gt;gamification&lt;/a&gt; (competing against yourself or friends),&lt;/li&gt;
&lt;li&gt;comprehensive statistics,&lt;/li&gt;
&lt;li&gt;or having someone else to control your usage.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Not everything is limited to blocking apps; there are also minimal app launchers&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn32&quot; id=&quot;fnref32&quot;&gt;[32]&lt;/a&gt;&lt;/sup&gt; or simple productivity timers&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn33&quot; id=&quot;fnref33&quot;&gt;[33]&lt;/a&gt;&lt;/sup&gt;. &lt;span class=&quot;apps-inline&quot;&gt;&lt;i&gt;minimalist phone&lt;/i&gt;&lt;span class=&quot;app-inline&quot;&gt; &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.qqlabs.minimalistlauncher&quot; target=&quot;_blank&quot;&gt;&lt;svg class=&quot;android-icon&quot; fill=&quot;#a4c639&quot; version=&quot;1.1&quot; id=&quot;Capa_1&quot; width=&quot;24&quot; height=&quot;16&quot; viewBox=&quot;0 0 307.42599 173.49678&quot; xml:space=&quot;preserve&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;defs id=&quot;defs2&quot;&gt;&lt;/defs&gt;&lt;g id=&quot;g2&quot; transform=&quot;translate(-122.651,-2.2205581e-4)&quot;&gt;&lt;g id=&quot;g1&quot;&gt;&lt;path d=&quot;M 351.972,50.847 375.57,7.315 c 1.549,-2.882 0.998,-5.092 -1.658,-6.646 -2.883,-1.34 -5.098,-0.661 -6.646,1.989 l -23.928,43.88 c -21.055,-9.309 -43.324,-13.972 -66.807,-13.972 -23.488,0 -45.759,4.664 -66.806,13.972 L 185.796,2.658 c -1.555,-2.65 -3.77,-3.323 -6.646,-1.989 -2.662,1.561 -3.213,3.764 -1.658,6.646 l 23.599,43.532 c -23.929,12.203 -42.987,29.198 -57.167,51.022 -14.18,21.836 -21.273,45.698 -21.273,71.628 h 307.426 c 0,-25.924 -7.094,-49.787 -21.273,-71.628 C 394.623,80.045 375.675,63.05 351.972,50.847 Z m -136.433,63.318 c -2.552,2.558 -5.6,3.831 -9.143,3.831 -3.55,0 -6.536,-1.273 -8.972,-3.831 -2.436,-2.546 -3.654,-5.582 -3.654,-9.137 0,-3.543 1.218,-6.585 3.654,-9.137 2.436,-2.546 5.429,-3.819 8.972,-3.819 3.543,0 6.591,1.273 9.143,3.819 2.546,2.558 3.825,5.594 3.825,9.137 -0.007,3.549 -1.285,6.591 -3.825,9.137 z m 140.086,0 c -2.441,2.558 -5.434,3.831 -8.971,3.831 -3.551,0 -6.598,-1.273 -9.145,-3.831 -2.551,-2.546 -3.824,-5.582 -3.824,-9.137 0,-3.543 1.273,-6.585 3.824,-9.137 2.547,-2.546 5.594,-3.819 9.145,-3.819 3.543,0 6.529,1.273 8.971,3.819 2.438,2.558 3.654,5.594 3.654,9.137 0,3.549 -1.217,6.591 -3.654,9.137 z&quot; id=&quot;path1&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt; does well in the “seamless experience” department by filtering notifications and taking over your launcher to control how you open apps. I don’t want to do an exhaustive analysis, but just searching “screentime” will already return many results on the app store. It’s worth downloading a few until you find your favorite anyway.&lt;/p&gt;
&lt;p&gt;However, manufacturers have it much easier, given that they have full system access over your phone. For instance, the open-source app &lt;span class=&quot;apps-inline&quot;&gt;&lt;i&gt;TimeLimit&lt;/i&gt;&lt;span class=&quot;app-inline&quot;&gt; &lt;a href=&quot;https://play.google.com/store/apps/details?id=io.timelimit.android.google.store&quot; target=&quot;_blank&quot;&gt;&lt;svg class=&quot;android-icon&quot; fill=&quot;#a4c639&quot; version=&quot;1.1&quot; id=&quot;Capa_1&quot; width=&quot;24&quot; height=&quot;16&quot; viewBox=&quot;0 0 307.42599 173.49678&quot; xml:space=&quot;preserve&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;defs id=&quot;defs2&quot;&gt;&lt;/defs&gt;&lt;g id=&quot;g2&quot; transform=&quot;translate(-122.651,-2.2205581e-4)&quot;&gt;&lt;g id=&quot;g1&quot;&gt;&lt;path d=&quot;M 351.972,50.847 375.57,7.315 c 1.549,-2.882 0.998,-5.092 -1.658,-6.646 -2.883,-1.34 -5.098,-0.661 -6.646,1.989 l -23.928,43.88 c -21.055,-9.309 -43.324,-13.972 -66.807,-13.972 -23.488,0 -45.759,4.664 -66.806,13.972 L 185.796,2.658 c -1.555,-2.65 -3.77,-3.323 -6.646,-1.989 -2.662,1.561 -3.213,3.764 -1.658,6.646 l 23.599,43.532 c -23.929,12.203 -42.987,29.198 -57.167,51.022 -14.18,21.836 -21.273,45.698 -21.273,71.628 h 307.426 c 0,-25.924 -7.094,-49.787 -21.273,-71.628 C 394.623,80.045 375.675,63.05 351.972,50.847 Z m -136.433,63.318 c -2.552,2.558 -5.6,3.831 -9.143,3.831 -3.55,0 -6.536,-1.273 -8.972,-3.831 -2.436,-2.546 -3.654,-5.582 -3.654,-9.137 0,-3.543 1.218,-6.585 3.654,-9.137 2.436,-2.546 5.429,-3.819 8.972,-3.819 3.543,0 6.591,1.273 9.143,3.819 2.546,2.558 3.825,5.594 3.825,9.137 -0.007,3.549 -1.285,6.591 -3.825,9.137 z m 140.086,0 c -2.441,2.558 -5.434,3.831 -8.971,3.831 -3.551,0 -6.598,-1.273 -9.145,-3.831 -2.551,-2.546 -3.824,-5.582 -3.824,-9.137 0,-3.543 1.273,-6.585 3.824,-9.137 2.547,-2.546 5.594,-3.819 9.145,-3.819 3.543,0 6.529,1.273 8.971,3.819 2.438,2.558 3.654,5.594 3.654,9.137 0,3.549 -1.217,6.591 -3.654,9.137 z&quot; id=&quot;path1&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt; is an even more configurable alternative to “Digital Wellbeing”. But being external, it needs to start with a long (and worrying) step to grant permissions. This alone is one step too many to make it widespread — I’d argue that even having to install an app is too much.&lt;/p&gt;
&lt;p&gt;To improve the user experience, some apps make emphasis on explaining how to use their features. Others avoid it by trying to be smarter; they have your current phone’s usage data, so they already know which apps you use too much. One last approach is to be opinionated and only support a subset of features that may integrate better or have more impact. The Light Phone 2 does this by only providing their limited list of features; if you’re missing one, maybe you’ll get it, but maybe not.&lt;/p&gt;
&lt;p&gt;Something else raising the barrier of entry is monetization. Although necessary, some subscription models can be too much. A particularly creative app I liked was &lt;span class=&quot;apps-inline&quot;&gt;&lt;i&gt;Digital Detox&lt;/i&gt;&lt;span class=&quot;app-inline&quot;&gt; &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.urbandroid.ddc&quot; target=&quot;_blank&quot;&gt;&lt;svg class=&quot;android-icon&quot; fill=&quot;#a4c639&quot; version=&quot;1.1&quot; id=&quot;Capa_1&quot; width=&quot;24&quot; height=&quot;16&quot; viewBox=&quot;0 0 307.42599 173.49678&quot; xml:space=&quot;preserve&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;defs id=&quot;defs2&quot;&gt;&lt;/defs&gt;&lt;g id=&quot;g2&quot; transform=&quot;translate(-122.651,-2.2205581e-4)&quot;&gt;&lt;g id=&quot;g1&quot;&gt;&lt;path d=&quot;M 351.972,50.847 375.57,7.315 c 1.549,-2.882 0.998,-5.092 -1.658,-6.646 -2.883,-1.34 -5.098,-0.661 -6.646,1.989 l -23.928,43.88 c -21.055,-9.309 -43.324,-13.972 -66.807,-13.972 -23.488,0 -45.759,4.664 -66.806,13.972 L 185.796,2.658 c -1.555,-2.65 -3.77,-3.323 -6.646,-1.989 -2.662,1.561 -3.213,3.764 -1.658,6.646 l 23.599,43.532 c -23.929,12.203 -42.987,29.198 -57.167,51.022 -14.18,21.836 -21.273,45.698 -21.273,71.628 h 307.426 c 0,-25.924 -7.094,-49.787 -21.273,-71.628 C 394.623,80.045 375.675,63.05 351.972,50.847 Z m -136.433,63.318 c -2.552,2.558 -5.6,3.831 -9.143,3.831 -3.55,0 -6.536,-1.273 -8.972,-3.831 -2.436,-2.546 -3.654,-5.582 -3.654,-9.137 0,-3.543 1.218,-6.585 3.654,-9.137 2.436,-2.546 5.429,-3.819 8.972,-3.819 3.543,0 6.591,1.273 9.143,3.819 2.546,2.558 3.825,5.594 3.825,9.137 -0.007,3.549 -1.285,6.591 -3.825,9.137 z m 140.086,0 c -2.441,2.558 -5.434,3.831 -8.971,3.831 -3.551,0 -6.598,-1.273 -9.145,-3.831 -2.551,-2.546 -3.824,-5.582 -3.824,-9.137 0,-3.543 1.273,-6.585 3.824,-9.137 2.547,-2.546 5.594,-3.819 9.145,-3.819 3.543,0 6.529,1.273 8.971,3.819 2.438,2.558 3.654,5.594 3.654,9.137 0,3.549 -1.217,6.591 -3.654,9.137 z&quot; id=&quot;path1&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;, which makes you pay $2 upon failing to meet your phone usage goals.&lt;/p&gt;
&lt;p&gt;It’s just great to have so many options and not being locked in to any of them. Different solutions for different people.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_accessories&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;accessories&quot; tabindex=&quot;-1&quot;&gt;Accessories &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/phone-intro/#accessories&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Some products allow you to disable apps based on physical access. Imagine a keychain with an NFC chip that can restrict apps on your phone. Having to find it and hold it near your phone can help break the habit of opening Instagram automatically, turning it into a conscious decision. These tools can also help transform your phone into a dumbphone: simply block the apps and leave the device at home to fully disconnect.&lt;/p&gt;
&lt;p&gt;A couple of options are &lt;a href=&quot;https://getbrick.app/&quot;&gt;Brick&lt;/a&gt; and &lt;a href=&quot;https://www.unpluq.com/&quot;&gt;UnPluq&lt;/a&gt;. They only solve part of the issue, though, and UnPluq follows a subscription-based model that costs 70€ per year. Still, they seem to work well for some folks, which is awesome.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_phones&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;phones&quot; tabindex=&quot;-1&quot;&gt;Phones &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/phone-intro/#phones&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;What would a phone minimizing screen time look like? Many of the popular ones have &lt;a href=&quot;https://en.wikipedia.org/wiki/Electronic_paper&quot;&gt;&lt;em&gt;e-paper&lt;/em&gt; displays&lt;/a&gt; instead of LCD, which is most commonly seen on e-readers. E-paper feels like real paper, is easier to see under sunlight, and may increase battery time. It doesn’t come without drawbacks, given that it literally moves physical particles in your screen instead of emitting light. You can judge yourself:&lt;/p&gt;
&lt;iframe loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;400&quot; src=&quot;https://www.youtube.com/embed/IFgxUr26A8g&quot; title=&quot;E ink phone | YouTube | Linus Tech Tips | Hisense A9&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;Hey, it’s not a good experience for videos, but it doesn’t take 5 seconds per refresh like your crappy 10-year-old Kindle. Knowing how it works under the hood, this sample is impressive to me. Here’s another monitor that recently came out focusing on latency:&lt;/p&gt;
&lt;iframe loading=&quot;lazy&quot; width=&quot;1600&quot; height=&quot;400&quot; src=&quot;https://www.youtube.com/embed/pXn-bAwzNv4?start=183&quot; title=&quot;Modos Paper Monitor Status Update&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share&quot; referrerpolicy=&quot;strict-origin-when-cross-origin&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;E-paper has always had a poor refresh rate. The issue isn’t just that videos are hard to watch — you shouldn’t do it often, anyway. The real problem is that sluggish animations worsen the user experience. Recently, a wave of e-paper products with faster refresh times&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn34&quot; id=&quot;fnref34&quot;&gt;[34]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn35&quot; id=&quot;fnref35&quot;&gt;[35]&lt;/a&gt;&lt;/sup&gt; has emerged, so I’m hoping that will improve.&lt;/p&gt;
&lt;p&gt;Another charasteristic of most e-paper screens is that they are grayscale. While the absence of colors is linked with reduced addiction&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn36&quot; id=&quot;fnref36&quot;&gt;[36]&lt;/a&gt;&lt;/sup&gt;, it can also be frustrating. I’ve set my phone to grayscale, and I know how confusing Google Maps can sometimes be without colors. Additionally, charts that rely on color require you to view them on a different device. And I haven’t even tried gaming. One could argue that this is intentional, to get you to use different devices for different purposes. Instead of playing Candy Crush on the train, you might read and wait until you get home to use your PS4.&lt;/p&gt;
&lt;p&gt;There’s now color e-paper, with Kobo having released its first e-readers in 2024&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn37&quot; id=&quot;fnref37&quot;&gt;[37]&lt;/a&gt;&lt;/sup&gt;. But it does have downsides, such as worse refresh rates or lower contrast ratios. Personally, I’d love to try to embrace the limitations of grayscale. Issues with essential apps like Google Maps could be resolved with custom software. And not having the best experience watching YouTube on your phone might be for good. It’s possible that having a single color like red could improve the user experience by highlighting important items, though.&lt;/p&gt;
&lt;p&gt;Nowadays, the biggest brands that go beyond e-readers are HiSense and Boox. However, they aren’t well-supported in the west. Some apps refuse to open, and connectivity only works with certain providers, if at all&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn38&quot; id=&quot;fnref38&quot;&gt;[38]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn39&quot; id=&quot;fnref39&quot;&gt;[39]&lt;/a&gt;&lt;/sup&gt;. Boox is known for violating GPL compliance, too&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn40&quot; id=&quot;fnref40&quot;&gt;[40]&lt;/a&gt;&lt;/sup&gt;. There are startups releasing similar devices, but they have a long road ahead: Mudita will announce a new phone soon&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn41&quot; id=&quot;fnref41&quot;&gt;[41]&lt;/a&gt;&lt;/sup&gt;, and Daylight might work on a phone if their $729 tablet is successful&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn42&quot; id=&quot;fnref42&quot;&gt;[42]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn43&quot; id=&quot;fnref43&quot;&gt;[43]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;For 360€, the Blloc Zero18 was one of the few phones that didn’t use e-paper but wasn’t a dumbphone either. It balanced full functionality with impressive features designed to keep you from opening apps at all&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn44&quot; id=&quot;fnref44&quot;&gt;[44]&lt;/a&gt;&lt;/sup&gt;. By default, its screen was grayscale, but tapping the fingerprint sensor would bring back the color. The homepage combined all your chats into a single feed, similar to &lt;a href=&quot;https://www.beeper.com/&quot;&gt;Beeper&lt;/a&gt;, and had interactive widgets for news, notes, or YouTube search. As you can tell from my use of the past sense, though, they ended up ditching the phone. The company shifted focus to just developing the launcher, and they ended up running out of money&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn45&quot; id=&quot;fnref45&quot;&gt;[45]&lt;/a&gt;&lt;/sup&gt;. Many employees have since joined &lt;a href=&quot;https://intl.nothing.tech&quot;&gt;Nothing&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Another notable flop&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn46&quot; id=&quot;fnref46&quot;&gt;[46]&lt;/a&gt;&lt;/sup&gt; was the &lt;a href=&quot;https://en.wikipedia.org/wiki/Yota&quot;&gt;YotaPhone&lt;/a&gt;. This unique phone featured an additional e-paper screen on the back, marketed for reading and basic tasks. As innovative as it was, you’d have to &lt;em&gt;really&lt;/em&gt; like reading to justify spending over $600 for a phone that was otherwise unimpressive&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn47&quot; id=&quot;fnref47&quot;&gt;[47]&lt;/a&gt;&lt;/sup&gt;. Unfortunately, it never gained popularity in Europe and was not released in the US&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn48&quot; id=&quot;fnref48&quot;&gt;[48]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn49&quot; id=&quot;fnref49&quot;&gt;[49]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn50&quot; id=&quot;fnref50&quot;&gt;[50]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn51&quot; id=&quot;fnref51&quot;&gt;[51]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;A simpler approach to consider is what &lt;a href=&quot;https://ghostmode.us/&quot;&gt;Ghost Mode&lt;/a&gt; does. They lock down a Pixel6a with their custom operating system and resell it. In the end, it’s essentially a dumbphone with a nice camera. They don’t need to deal with manufacturing, and the software still has system access for advanced features. I’m only afraid that relying on Google might not be a good idea&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fn52&quot; id=&quot;fnref52&quot;&gt;[52]&lt;/a&gt;&lt;/sup&gt;, but they could switch to a different base phone. Its unpopularity might also have to do with money: at $600, it’s pricey for a dumbphone.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_wrapping_up&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;wrapping-up&quot; tabindex=&quot;-1&quot;&gt;Wrapping up &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/phone-intro/#wrapping-up&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I hope this topic can eventually be “a thing”. Just like there are movements for “sustainability” or “diversity”, there should also be one for “reduced screentime”. To me, it has a strong relationship with mental health, and there’s a lot to improve in that regard. I love the internet: being able to share this post so easily is wonderful. But what can we do to reduce the bad parts?&lt;/p&gt;
&lt;p&gt;We still don’t fully know what this hybrid between a smartphone and a dumbphone looks like. However, this research has taught us many essential things: the relevant organizations, its societal impact, how to build the company, existing software and hardware solutions, and some history.&lt;/p&gt;
&lt;p&gt;More details about the design will come later in &lt;a href=&quot;https://nullderef.com/series/dont-use-this-phone/&quot;&gt;the series&lt;/a&gt;. You can &lt;a href=&quot;https://nullderef.com/subscribe&quot;&gt;subscribe&lt;/a&gt; for free to keep up to date. It’s hard to say yet, but I’d love to build something in this area in the future. Manufacturing a phone sounds crazy, but I’d love to see what can be done :)&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Disclaimer: I am not affiliated with any of the companies mentioned in this post. The opinions expressed are my own and are based on my personal experiences and research.&lt;/em&gt;&lt;/p&gt;

&lt;script src=&quot;https://f.convertkit.com/ckjs/ck.5.js&quot;&gt;&lt;/script&gt;
&lt;form action=&quot;https://app.kit.com/forms/7534343/subscriptions&quot; class=&quot;seva-form formkit-form&quot; method=&quot;post&quot; data-sv-form=&quot;7534343&quot; data-uid=&quot;3680381778&quot; data-format=&quot;inline&quot; data-version=&quot;5&quot; data-options=&#39;{&quot;settings&quot;:{&quot;after_subscribe&quot;:{&quot;action&quot;:&quot;message&quot;,&quot;success_message&quot;:&quot;Success! Check your email to confirm your subscription. If you can’t find it, look in the spam folder.&quot;,&quot;redirect_url&quot;:&quot;&quot;},&quot;analytics&quot;:{&quot;google&quot;:null,&quot;fathom&quot;:null,&quot;facebook&quot;:null,&quot;segment&quot;:null,&quot;pinterest&quot;:null,&quot;sparkloop&quot;:null,&quot;googletagmanager&quot;:null},&quot;modal&quot;:{&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;powered_by&quot;:{&quot;show&quot;:true,&quot;url&quot;:&quot;https://kit.com/features/forms?utm_campaign=poweredby&amp;amp;utm_content=form&amp;amp;utm_medium=referral&amp;amp;utm_source=dynamic&quot;},&quot;recaptcha&quot;:{&quot;enabled&quot;:false},&quot;return_visitor&quot;:{&quot;action&quot;:&quot;show&quot;,&quot;custom_content&quot;:&quot;&quot;},&quot;slide_in&quot;:{&quot;display_in&quot;:&quot;bottom_right&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;sticky_bar&quot;:{&quot;display_in&quot;:&quot;top&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15}},&quot;version&quot;:&quot;5&quot;}&#39; min-width=&quot;400 500 600 700 800&quot;&gt;
  &lt;div data-style=&quot;clean&quot;&gt;
    &lt;div class=&quot;formkit-header&quot; data-element=&quot;header&quot;&gt;
      &lt;h2&gt;Want to receive an email when I post?&lt;/h2&gt;
    &lt;/div&gt;
    &lt;ul class=&quot;formkit-alert formkit-alert-error&quot; data-element=&quot;errors&quot; data-group=&quot;alert&quot;&gt;&lt;/ul&gt;
    &lt;div data-element=&quot;fields&quot; data-stacked=&quot;false&quot; class=&quot;seva-fields formkit-fields&quot;&gt;
      &lt;div class=&quot;formkit-field&quot;&gt;
        &lt;input class=&quot;formkit-input&quot; name=&quot;email_address&quot; aria-label=&quot;Email Address&quot; placeholder=&quot;Email Address&quot; required=&quot;&quot; type=&quot;email&quot;&gt;
      &lt;/div&gt;
      &lt;button data-element=&quot;submit&quot; class=&quot;formkit-submit&quot; style=&quot;background-color:#e06c75;font-weight:700&quot;&gt;
        &lt;div class=&quot;formkit-spinner&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span class=&quot;&quot;&gt;Subscribe&lt;/span&gt;
      &lt;/button&gt;
    &lt;/div&gt;
    &lt;div class=&quot;formkit-guarantee&quot; data-element=&quot;guarantee&quot;&gt;
      &lt;p&gt;&lt;em&gt;Free and without spam. You can also use my &lt;a href=&quot;https://nullderef.com/index.xml&quot; target=&quot;_blank&quot;&gt;RSS&lt;/a&gt;, &lt;a href=&quot;https://x.com/null_deref&quot; target=&quot;_blank&quot;&gt;X&lt;/a&gt;, or &lt;a href=&quot;https://mstdn.social/@nullderef&quot; target=&quot;_blank&quot;&gt;Mastodon&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
      &lt;p style=&quot;opacity:.6;font-size:16px&quot;&gt;&lt;em&gt;Powered by &lt;a href=&quot;https://kit.com/&quot; target=&quot;_black&quot;&gt;Kit&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;style&gt;.formkit-form[data-uid=&quot;3680381778&quot;] a{text-decoration:underline}.formkit-form[data-uid=&quot;3680381778&quot;] legend{border:none;font-size:inherit;margin-bottom:10px;padding:0;position:relative;display:table}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-checkboxes,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-select{width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{border:0;border-radius:var(--radius);color:#fff;cursor:pointer;display:inline-block;text-align:center;font-weight:500;cursor:pointer;margin-bottom:15px;overflow:hidden;padding:0;position:relative;vertical-align:middle}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover{outline:0}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover&gt;span{background-color:rgba(0,0,0,.1)}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit&gt;span{display:block;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out;padding:12px 24px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input{background:#fff;padding:12px;border:1px solid var(--border);border-radius:var(--radius);-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;line-height:1.4;margin:0;-webkit-transition:border-color ease-out .3s;transition:border-color ease-out .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:focus{outline:0;border-color:#1677be;-webkit-transition:border-color ease .3s;transition:border-color ease .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-webkit-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-moz-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:-ms-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert{background:#f9fafb;border:1px solid #e3e3e3;border-radius:5px;-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;list-style:none;margin:25px auto;padding:12px;text-align:center;width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert:empty{display:none}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-success{background:#d3fbeb;border-color:#10bf7a;color:#0c905c}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-error{background:#fde8e2;border-color:#f2643b;color:#ea4110}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;height:0;width:0;margin:0 auto;position:absolute;top:0;left:0;right:0;width:0;overflow:hidden;text-align:center;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div{margin:auto;width:12px;height:12px;background-color:#fff;opacity:.3;border-radius:100%;display:inline-block;-webkit-animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both;animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:first-child{-webkit-animation-delay:-.32s;animation-delay:-.32s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:nth-child(2){-webkit-animation-delay:-.16s;animation-delay:-.16s}.formkit-form[data-uid=&quot;3680381778&quot;]{border:1px solid var(--border);border-radius:var(--radius);position:relative;overflow:hidden;background:var(--code-bg)}.formkit-form[data-uid=&quot;3680381778&quot;]{padding:20px;width:100%;position:relative}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-header{margin:0 0 27px 0;font-size:20px;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee{margin-top:10px;margin-bottom:32px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee&gt;p{margin:0;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-fields{display:flex;flex-wrap:wrap;margin:25px auto 0 auto;gap:10px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-field,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{flex:1 0 100%}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;]{padding:0 30px}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-field{flex:100 1 auto}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-submit{flex:1 1 auto}&lt;/style&gt;
&lt;/form&gt;
&lt;h2 id=&quot;footnotes&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.theverge.com/2019/9/4/20847717/light-phone-2-minimalist-features-design-keyboard-crowdfunding&quot;&gt;The high hopes of the low-tech phone — The Verge&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.reddit.com/r/dumbphones/comments/sjtkm2/i_have_to_use_an_app_to_open_my_apartment_complex/&quot;&gt;I have to use an app to open my apartment complex parking gate, the app is called Gatewise. My lease does not mention anything about needing a smartphone or the use of any apps for garage access. Street parking is not an option. I just want technological equity — r/dumbphones&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.reddit.com/r/nosurf/comments/p73msh/digital_minimalism_reading_list/&quot;&gt;Digital Minimalism Reading List — r/NoSurf&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn4&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.wired.com/story/apple-investors-iphone-kids-depression-suicide-evidence/&quot;&gt;Apple investors say iPhones cause teen depression. Science doesn’t — Wired&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref4&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn5&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=LKPwKFigF8U&quot;&gt;Why Boredom is Good For You — YouTube, Veritasium&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref5&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn6&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=uuCoyILqut8&quot;&gt;Louis CK Embrace Your Loneliness — YouTube, The Impossible Conversation&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref6&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn7&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://arstechnica.com/gadgets/2021/05/iphones-night-shift-feature-doesnt-help-you-sleep-better-study-finds/&quot;&gt;Study: Using Apple’s Night Shift to improve your sleep? Don’t bother — arstechnica&lt;/a&gt; (&lt;em&gt;Quote: “it is important to think about what portion of that stimulation is light emission versus other cognitive and psychological stimulations”&lt;/em&gt;) &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref7&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn8&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.nytimes.com/2024/01/26/health/cars-phones-accidents.html&quot;&gt;Phones Track Everything but Their Role in Car Wrecks — The New York Times&lt;/a&gt; (&lt;em&gt;In summary, the exact number is unknown. &lt;a href=&quot;https://www.prnewswire.com/news-releases/national-safety-council-estimates-that-at-least-16-million-crashes-are-caused-each-year-by-drivers-using-cell-phones-and-texting-81252807.html&quot;&gt;This NSC report&lt;/a&gt; estimates it to be 1.6 million crashes, but it’s not precise and from 2010&lt;/em&gt;) &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref8&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn9&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://ledstrain.org/&quot;&gt;LEDStrain Forum&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref9&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn10&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.reddit.com/r/ChronicPain/comments/b936z9/has_anyone_here_been_diagnosed_with_central/&quot;&gt;Has anyone here been diagnosed with central sensitization and/or relate somehow to my story? (36M, pain started at 33) — r/ChronicPain&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref10&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn11&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://news.gallup.com/poll/393785/americans-close-wary-bond-smartphone.aspx&quot;&gt;Americans Have Close but Wary Bond With Their Smartphone — Gallup&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref11&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn12&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://37signals.com/&quot;&gt;37signals (the company that owns Basecamp)&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref12&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn13&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://basecamp.com/handbook&quot;&gt;The 37signals Employee Handbook&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref13&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn14&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://signalvnoise.com/&quot;&gt;Signal v. Noise (37signals’ former blog)&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref14&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn15&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.reddit.com/r/SaaS/comments/nrjsao/im_40_years_old_and_i_finally_bootstrapped_a_saas/&quot;&gt;I’m 40 years old and I finally bootstrapped a SaaS, Transistor.fm, to millions in revenue (with a co-founder!) — r/SaaS&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref15&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn16&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://saas.transistor.fm/episodes&quot;&gt;Build Your SaaS — transistor.fm&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref16&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn17&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/TransistorFM/handbook/blob/master/values.md&quot;&gt;What are our values? — GitHub TransistorFM/handbook&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref17&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn18&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://justinjackson.ca/&quot;&gt;Justin Jackson (co-founder of Transistor.fm)&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref18&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn19&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://wildbit.com/&quot;&gt;Wildbit&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref19&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn20&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://convertkit.com/handbook&quot;&gt;The ConvertKit Team Handbook&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref20&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn21&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=jT5rRh9AZf4&quot;&gt;Distracted? Let’s make technology that helps us spend our time well | Tristan Harris | TEDxBrussels — YouTube, TEDx Talks&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref21&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn22&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.inverse.com/input/features/rise-and-ruin-of-couchsurfing&quot;&gt;Paradise lost: The rise and ruin of Couchsurfing.com — Input&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref22&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn23&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://37signals.com/01&quot;&gt;An obligation to independence — 37signals (the company that owns Basecamp)&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref23&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn24&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://youtu.be/dDI9h4ool-E?t=1549&quot;&gt;Nothing CEO Carl Pei on the Phone 2 and the future of gadgets | The Vergecast — YouTube&lt;/a&gt; @ 25:49 &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref24&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn25&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.businessinsider.com/light-phone-2-dumb-phone-price-release-date-specs-2019-9&quot;&gt;This credit-card-size phone can do only 3 things and doesn’t have any apps — and it may be the key to freeing us from our smartphones — Business Insider&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref25&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref25:1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn26&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.indiegogo.com/projects/light-phone-2&quot;&gt;Light Phone 2 — IndieGoGo&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref26&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn27&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://venturebeat.com/media/remarkable-raises-15-million-to-bring-its-e-paper-tablets-to-more-scribblers/&quot;&gt;Remarkable raises $15 million to bring its e-paper tablets to more scribblers — VentureBeat&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref27&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn28&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://archive.ph/4FODk&quot;&gt;Mudita new technology company co-founder of CD Projekt — eurogamer.pl (archive)&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref28&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn29&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://openai.com/our-structure/&quot;&gt;Our structure — OpenAI&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref29&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn30&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Digital control:
&lt;span class=&quot;apps-inline&quot;&gt;&lt;i&gt;AppBlock&lt;/i&gt;&lt;span class=&quot;app-inline&quot;&gt; &lt;a href=&quot;https://play.google.com/store/apps/details?id=cz.mobilesoft.appblock&quot; target=&quot;_blank&quot;&gt;&lt;svg class=&quot;android-icon&quot; fill=&quot;#a4c639&quot; version=&quot;1.1&quot; id=&quot;Capa_1&quot; width=&quot;24&quot; height=&quot;16&quot; viewBox=&quot;0 0 307.42599 173.49678&quot; xml:space=&quot;preserve&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;defs id=&quot;defs2&quot;&gt;&lt;/defs&gt;&lt;g id=&quot;g2&quot; transform=&quot;translate(-122.651,-2.2205581e-4)&quot;&gt;&lt;g id=&quot;g1&quot;&gt;&lt;path d=&quot;M 351.972,50.847 375.57,7.315 c 1.549,-2.882 0.998,-5.092 -1.658,-6.646 -2.883,-1.34 -5.098,-0.661 -6.646,1.989 l -23.928,43.88 c -21.055,-9.309 -43.324,-13.972 -66.807,-13.972 -23.488,0 -45.759,4.664 -66.806,13.972 L 185.796,2.658 c -1.555,-2.65 -3.77,-3.323 -6.646,-1.989 -2.662,1.561 -3.213,3.764 -1.658,6.646 l 23.599,43.532 c -23.929,12.203 -42.987,29.198 -57.167,51.022 -14.18,21.836 -21.273,45.698 -21.273,71.628 h 307.426 c 0,-25.924 -7.094,-49.787 -21.273,-71.628 C 394.623,80.045 375.675,63.05 351.972,50.847 Z m -136.433,63.318 c -2.552,2.558 -5.6,3.831 -9.143,3.831 -3.55,0 -6.536,-1.273 -8.972,-3.831 -2.436,-2.546 -3.654,-5.582 -3.654,-9.137 0,-3.543 1.218,-6.585 3.654,-9.137 2.436,-2.546 5.429,-3.819 8.972,-3.819 3.543,0 6.591,1.273 9.143,3.819 2.546,2.558 3.825,5.594 3.825,9.137 -0.007,3.549 -1.285,6.591 -3.825,9.137 z m 140.086,0 c -2.441,2.558 -5.434,3.831 -8.971,3.831 -3.551,0 -6.598,-1.273 -9.145,-3.831 -2.551,-2.546 -3.824,-5.582 -3.824,-9.137 0,-3.543 1.273,-6.585 3.824,-9.137 2.547,-2.546 5.594,-3.819 9.145,-3.819 3.543,0 6.529,1.273 8.971,3.819 2.438,2.558 3.654,5.594 3.654,9.137 0,3.549 -1.217,6.591 -3.654,9.137 z&quot; id=&quot;path1&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;,
&lt;span class=&quot;apps-inline&quot;&gt;&lt;i&gt;Freedom&lt;/i&gt;&lt;span class=&quot;app-inline&quot;&gt; &lt;a href=&quot;https://apps.apple.com/us/app/freedom-screen-time-control/id1269788228&quot; target=&quot;_blank&quot;&gt;&lt;svg class=&quot;ios-icon&quot; xml:space=&quot;preserve&quot; width=&quot;17&quot; height=&quot;20&quot; version=&quot;1.1&quot; id=&quot;svg1&quot; viewBox=&quot;0 0 814.09996 999.89999&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;defs id=&quot;defs1&quot;&gt;&lt;/defs&gt;&lt;path d=&quot;m 788.1,340.9 c -5.8,4.5 -108.2,62.2 -108.2,190.5 0,148.4 130.3,200.9 134.2,202.2 -0.6,3.2 -20.7,71.9 -68.7,141.9 -42.8,61.6 -87.5,123.1 -155.5,123.1 -68,0 -85.5,-39.5 -164,-39.5 -76.5,0 -103.7,40.8 -165.9,40.8 -62.2,0 -105.6,-57 -155.5,-127 C 46.7,790.7 0,663 0,541.8 0,347.4 126.4,244.3 250.8,244.3 c 66.1,0 121.2,43.4 162.7,43.4 39.5,0 101.1,-46 176.3,-46 28.5,0 130.9,2.6 198.3,99.2 z M 554.1,159.4 C 585.2,122.5 607.2,71.3 607.2,20.1 607.2,13 606.6,5.8 605.3,0 554.7,1.9 494.5,33.7 458.2,75.8 c -28.5,32.4 -55.1,83.6 -55.1,135.5 0,7.8 1.3,15.6 1.9,18.1 3.2,0.6 8.4,1.3 13.6,1.3 45.4,0 102.5,-30.4 135.5,-71.3 z&quot; id=&quot;path1&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/span&gt;&lt;span class=&quot;app-inline&quot;&gt; &lt;a href=&quot;https://play.google.com/store/apps/details?id=to.freedom.android2&quot; target=&quot;_blank&quot;&gt;&lt;svg class=&quot;android-icon&quot; fill=&quot;#a4c639&quot; version=&quot;1.1&quot; id=&quot;Capa_1&quot; width=&quot;24&quot; height=&quot;16&quot; viewBox=&quot;0 0 307.42599 173.49678&quot; xml:space=&quot;preserve&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;defs id=&quot;defs2&quot;&gt;&lt;/defs&gt;&lt;g id=&quot;g2&quot; transform=&quot;translate(-122.651,-2.2205581e-4)&quot;&gt;&lt;g id=&quot;g1&quot;&gt;&lt;path d=&quot;M 351.972,50.847 375.57,7.315 c 1.549,-2.882 0.998,-5.092 -1.658,-6.646 -2.883,-1.34 -5.098,-0.661 -6.646,1.989 l -23.928,43.88 c -21.055,-9.309 -43.324,-13.972 -66.807,-13.972 -23.488,0 -45.759,4.664 -66.806,13.972 L 185.796,2.658 c -1.555,-2.65 -3.77,-3.323 -6.646,-1.989 -2.662,1.561 -3.213,3.764 -1.658,6.646 l 23.599,43.532 c -23.929,12.203 -42.987,29.198 -57.167,51.022 -14.18,21.836 -21.273,45.698 -21.273,71.628 h 307.426 c 0,-25.924 -7.094,-49.787 -21.273,-71.628 C 394.623,80.045 375.675,63.05 351.972,50.847 Z m -136.433,63.318 c -2.552,2.558 -5.6,3.831 -9.143,3.831 -3.55,0 -6.536,-1.273 -8.972,-3.831 -2.436,-2.546 -3.654,-5.582 -3.654,-9.137 0,-3.543 1.218,-6.585 3.654,-9.137 2.436,-2.546 5.429,-3.819 8.972,-3.819 3.543,0 6.591,1.273 9.143,3.819 2.546,2.558 3.825,5.594 3.825,9.137 -0.007,3.549 -1.285,6.591 -3.825,9.137 z m 140.086,0 c -2.441,2.558 -5.434,3.831 -8.971,3.831 -3.551,0 -6.598,-1.273 -9.145,-3.831 -2.551,-2.546 -3.824,-5.582 -3.824,-9.137 0,-3.543 1.273,-6.585 3.824,-9.137 2.547,-2.546 5.594,-3.819 9.145,-3.819 3.543,0 6.529,1.273 8.971,3.819 2.438,2.558 3.654,5.594 3.654,9.137 0,3.549 -1.217,6.591 -3.654,9.137 z&quot; id=&quot;path1&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;,
&lt;span class=&quot;apps-inline&quot;&gt;&lt;i&gt;YourHour&lt;/i&gt;&lt;span class=&quot;app-inline&quot;&gt; &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.mindefy.phoneaddiction.mobilepe&quot; target=&quot;_blank&quot;&gt;&lt;svg class=&quot;android-icon&quot; fill=&quot;#a4c639&quot; version=&quot;1.1&quot; id=&quot;Capa_1&quot; width=&quot;24&quot; height=&quot;16&quot; viewBox=&quot;0 0 307.42599 173.49678&quot; xml:space=&quot;preserve&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;defs id=&quot;defs2&quot;&gt;&lt;/defs&gt;&lt;g id=&quot;g2&quot; transform=&quot;translate(-122.651,-2.2205581e-4)&quot;&gt;&lt;g id=&quot;g1&quot;&gt;&lt;path d=&quot;M 351.972,50.847 375.57,7.315 c 1.549,-2.882 0.998,-5.092 -1.658,-6.646 -2.883,-1.34 -5.098,-0.661 -6.646,1.989 l -23.928,43.88 c -21.055,-9.309 -43.324,-13.972 -66.807,-13.972 -23.488,0 -45.759,4.664 -66.806,13.972 L 185.796,2.658 c -1.555,-2.65 -3.77,-3.323 -6.646,-1.989 -2.662,1.561 -3.213,3.764 -1.658,6.646 l 23.599,43.532 c -23.929,12.203 -42.987,29.198 -57.167,51.022 -14.18,21.836 -21.273,45.698 -21.273,71.628 h 307.426 c 0,-25.924 -7.094,-49.787 -21.273,-71.628 C 394.623,80.045 375.675,63.05 351.972,50.847 Z m -136.433,63.318 c -2.552,2.558 -5.6,3.831 -9.143,3.831 -3.55,0 -6.536,-1.273 -8.972,-3.831 -2.436,-2.546 -3.654,-5.582 -3.654,-9.137 0,-3.543 1.218,-6.585 3.654,-9.137 2.436,-2.546 5.429,-3.819 8.972,-3.819 3.543,0 6.591,1.273 9.143,3.819 2.546,2.558 3.825,5.594 3.825,9.137 -0.007,3.549 -1.285,6.591 -3.825,9.137 z m 140.086,0 c -2.441,2.558 -5.434,3.831 -8.971,3.831 -3.551,0 -6.598,-1.273 -9.145,-3.831 -2.551,-2.546 -3.824,-5.582 -3.824,-9.137 0,-3.543 1.273,-6.585 3.824,-9.137 2.547,-2.546 5.594,-3.819 9.145,-3.819 3.543,0 6.529,1.273 8.971,3.819 2.438,2.558 3.654,5.594 3.654,9.137 0,3.549 -1.217,6.591 -3.654,9.137 z&quot; id=&quot;path1&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;,
&lt;span class=&quot;apps-inline&quot;&gt;&lt;i&gt;Digital Detox&lt;/i&gt;&lt;span class=&quot;app-inline&quot;&gt; &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.urbandroid.ddc&quot; target=&quot;_blank&quot;&gt;&lt;svg class=&quot;android-icon&quot; fill=&quot;#a4c639&quot; version=&quot;1.1&quot; id=&quot;Capa_1&quot; width=&quot;24&quot; height=&quot;16&quot; viewBox=&quot;0 0 307.42599 173.49678&quot; xml:space=&quot;preserve&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;defs id=&quot;defs2&quot;&gt;&lt;/defs&gt;&lt;g id=&quot;g2&quot; transform=&quot;translate(-122.651,-2.2205581e-4)&quot;&gt;&lt;g id=&quot;g1&quot;&gt;&lt;path d=&quot;M 351.972,50.847 375.57,7.315 c 1.549,-2.882 0.998,-5.092 -1.658,-6.646 -2.883,-1.34 -5.098,-0.661 -6.646,1.989 l -23.928,43.88 c -21.055,-9.309 -43.324,-13.972 -66.807,-13.972 -23.488,0 -45.759,4.664 -66.806,13.972 L 185.796,2.658 c -1.555,-2.65 -3.77,-3.323 -6.646,-1.989 -2.662,1.561 -3.213,3.764 -1.658,6.646 l 23.599,43.532 c -23.929,12.203 -42.987,29.198 -57.167,51.022 -14.18,21.836 -21.273,45.698 -21.273,71.628 h 307.426 c 0,-25.924 -7.094,-49.787 -21.273,-71.628 C 394.623,80.045 375.675,63.05 351.972,50.847 Z m -136.433,63.318 c -2.552,2.558 -5.6,3.831 -9.143,3.831 -3.55,0 -6.536,-1.273 -8.972,-3.831 -2.436,-2.546 -3.654,-5.582 -3.654,-9.137 0,-3.543 1.218,-6.585 3.654,-9.137 2.436,-2.546 5.429,-3.819 8.972,-3.819 3.543,0 6.591,1.273 9.143,3.819 2.546,2.558 3.825,5.594 3.825,9.137 -0.007,3.549 -1.285,6.591 -3.825,9.137 z m 140.086,0 c -2.441,2.558 -5.434,3.831 -8.971,3.831 -3.551,0 -6.598,-1.273 -9.145,-3.831 -2.551,-2.546 -3.824,-5.582 -3.824,-9.137 0,-3.543 1.273,-6.585 3.824,-9.137 2.547,-2.546 5.594,-3.819 9.145,-3.819 3.543,0 6.529,1.273 8.971,3.819 2.438,2.558 3.654,5.594 3.654,9.137 0,3.549 -1.217,6.591 -3.654,9.137 z&quot; id=&quot;path1&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;,
&lt;span class=&quot;apps-inline&quot;&gt;&lt;i&gt;StayFree&lt;/i&gt;&lt;span class=&quot;app-inline&quot;&gt; &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.burockgames.timeclocker&quot; target=&quot;_blank&quot;&gt;&lt;svg class=&quot;android-icon&quot; fill=&quot;#a4c639&quot; version=&quot;1.1&quot; id=&quot;Capa_1&quot; width=&quot;24&quot; height=&quot;16&quot; viewBox=&quot;0 0 307.42599 173.49678&quot; xml:space=&quot;preserve&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;defs id=&quot;defs2&quot;&gt;&lt;/defs&gt;&lt;g id=&quot;g2&quot; transform=&quot;translate(-122.651,-2.2205581e-4)&quot;&gt;&lt;g id=&quot;g1&quot;&gt;&lt;path d=&quot;M 351.972,50.847 375.57,7.315 c 1.549,-2.882 0.998,-5.092 -1.658,-6.646 -2.883,-1.34 -5.098,-0.661 -6.646,1.989 l -23.928,43.88 c -21.055,-9.309 -43.324,-13.972 -66.807,-13.972 -23.488,0 -45.759,4.664 -66.806,13.972 L 185.796,2.658 c -1.555,-2.65 -3.77,-3.323 -6.646,-1.989 -2.662,1.561 -3.213,3.764 -1.658,6.646 l 23.599,43.532 c -23.929,12.203 -42.987,29.198 -57.167,51.022 -14.18,21.836 -21.273,45.698 -21.273,71.628 h 307.426 c 0,-25.924 -7.094,-49.787 -21.273,-71.628 C 394.623,80.045 375.675,63.05 351.972,50.847 Z m -136.433,63.318 c -2.552,2.558 -5.6,3.831 -9.143,3.831 -3.55,0 -6.536,-1.273 -8.972,-3.831 -2.436,-2.546 -3.654,-5.582 -3.654,-9.137 0,-3.543 1.218,-6.585 3.654,-9.137 2.436,-2.546 5.429,-3.819 8.972,-3.819 3.543,0 6.591,1.273 9.143,3.819 2.546,2.558 3.825,5.594 3.825,9.137 -0.007,3.549 -1.285,6.591 -3.825,9.137 z m 140.086,0 c -2.441,2.558 -5.434,3.831 -8.971,3.831 -3.551,0 -6.598,-1.273 -9.145,-3.831 -2.551,-2.546 -3.824,-5.582 -3.824,-9.137 0,-3.543 1.273,-6.585 3.824,-9.137 2.547,-2.546 5.594,-3.819 9.145,-3.819 3.543,0 6.529,1.273 8.971,3.819 2.438,2.558 3.654,5.594 3.654,9.137 0,3.549 -1.217,6.591 -3.654,9.137 z&quot; id=&quot;path1&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;,
&lt;span class=&quot;apps-inline&quot;&gt;&lt;i&gt;Stay Focused&lt;/i&gt;&lt;span class=&quot;app-inline&quot;&gt; &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.stayfocused&quot; target=&quot;_blank&quot;&gt;&lt;svg class=&quot;android-icon&quot; fill=&quot;#a4c639&quot; version=&quot;1.1&quot; id=&quot;Capa_1&quot; width=&quot;24&quot; height=&quot;16&quot; viewBox=&quot;0 0 307.42599 173.49678&quot; xml:space=&quot;preserve&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;defs id=&quot;defs2&quot;&gt;&lt;/defs&gt;&lt;g id=&quot;g2&quot; transform=&quot;translate(-122.651,-2.2205581e-4)&quot;&gt;&lt;g id=&quot;g1&quot;&gt;&lt;path d=&quot;M 351.972,50.847 375.57,7.315 c 1.549,-2.882 0.998,-5.092 -1.658,-6.646 -2.883,-1.34 -5.098,-0.661 -6.646,1.989 l -23.928,43.88 c -21.055,-9.309 -43.324,-13.972 -66.807,-13.972 -23.488,0 -45.759,4.664 -66.806,13.972 L 185.796,2.658 c -1.555,-2.65 -3.77,-3.323 -6.646,-1.989 -2.662,1.561 -3.213,3.764 -1.658,6.646 l 23.599,43.532 c -23.929,12.203 -42.987,29.198 -57.167,51.022 -14.18,21.836 -21.273,45.698 -21.273,71.628 h 307.426 c 0,-25.924 -7.094,-49.787 -21.273,-71.628 C 394.623,80.045 375.675,63.05 351.972,50.847 Z m -136.433,63.318 c -2.552,2.558 -5.6,3.831 -9.143,3.831 -3.55,0 -6.536,-1.273 -8.972,-3.831 -2.436,-2.546 -3.654,-5.582 -3.654,-9.137 0,-3.543 1.218,-6.585 3.654,-9.137 2.436,-2.546 5.429,-3.819 8.972,-3.819 3.543,0 6.591,1.273 9.143,3.819 2.546,2.558 3.825,5.594 3.825,9.137 -0.007,3.549 -1.285,6.591 -3.825,9.137 z m 140.086,0 c -2.441,2.558 -5.434,3.831 -8.971,3.831 -3.551,0 -6.598,-1.273 -9.145,-3.831 -2.551,-2.546 -3.824,-5.582 -3.824,-9.137 0,-3.543 1.273,-6.585 3.824,-9.137 2.547,-2.546 5.594,-3.819 9.145,-3.819 3.543,0 6.529,1.273 8.971,3.819 2.438,2.558 3.654,5.594 3.654,9.137 0,3.549 -1.217,6.591 -3.654,9.137 z&quot; id=&quot;path1&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;,
&lt;span class=&quot;apps-inline&quot;&gt;&lt;i&gt;StayOff&lt;/i&gt;&lt;span class=&quot;app-inline&quot;&gt; &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.app.floatingapptimer.com&quot; target=&quot;_blank&quot;&gt;&lt;svg class=&quot;android-icon&quot; fill=&quot;#a4c639&quot; version=&quot;1.1&quot; id=&quot;Capa_1&quot; width=&quot;24&quot; height=&quot;16&quot; viewBox=&quot;0 0 307.42599 173.49678&quot; xml:space=&quot;preserve&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;defs id=&quot;defs2&quot;&gt;&lt;/defs&gt;&lt;g id=&quot;g2&quot; transform=&quot;translate(-122.651,-2.2205581e-4)&quot;&gt;&lt;g id=&quot;g1&quot;&gt;&lt;path d=&quot;M 351.972,50.847 375.57,7.315 c 1.549,-2.882 0.998,-5.092 -1.658,-6.646 -2.883,-1.34 -5.098,-0.661 -6.646,1.989 l -23.928,43.88 c -21.055,-9.309 -43.324,-13.972 -66.807,-13.972 -23.488,0 -45.759,4.664 -66.806,13.972 L 185.796,2.658 c -1.555,-2.65 -3.77,-3.323 -6.646,-1.989 -2.662,1.561 -3.213,3.764 -1.658,6.646 l 23.599,43.532 c -23.929,12.203 -42.987,29.198 -57.167,51.022 -14.18,21.836 -21.273,45.698 -21.273,71.628 h 307.426 c 0,-25.924 -7.094,-49.787 -21.273,-71.628 C 394.623,80.045 375.675,63.05 351.972,50.847 Z m -136.433,63.318 c -2.552,2.558 -5.6,3.831 -9.143,3.831 -3.55,0 -6.536,-1.273 -8.972,-3.831 -2.436,-2.546 -3.654,-5.582 -3.654,-9.137 0,-3.543 1.218,-6.585 3.654,-9.137 2.436,-2.546 5.429,-3.819 8.972,-3.819 3.543,0 6.591,1.273 9.143,3.819 2.546,2.558 3.825,5.594 3.825,9.137 -0.007,3.549 -1.285,6.591 -3.825,9.137 z m 140.086,0 c -2.441,2.558 -5.434,3.831 -8.971,3.831 -3.551,0 -6.598,-1.273 -9.145,-3.831 -2.551,-2.546 -3.824,-5.582 -3.824,-9.137 0,-3.543 1.273,-6.585 3.824,-9.137 2.547,-2.546 5.594,-3.819 9.145,-3.819 3.543,0 6.529,1.273 8.971,3.819 2.438,2.558 3.654,5.594 3.654,9.137 0,3.549 -1.217,6.591 -3.654,9.137 z&quot; id=&quot;path1&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;,
&lt;span class=&quot;apps-inline&quot;&gt;&lt;i&gt;ActionDash&lt;/i&gt;&lt;span class=&quot;app-inline&quot;&gt; &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.actiondash.playstore&quot; target=&quot;_blank&quot;&gt;&lt;svg class=&quot;android-icon&quot; fill=&quot;#a4c639&quot; version=&quot;1.1&quot; id=&quot;Capa_1&quot; width=&quot;24&quot; height=&quot;16&quot; viewBox=&quot;0 0 307.42599 173.49678&quot; xml:space=&quot;preserve&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;defs id=&quot;defs2&quot;&gt;&lt;/defs&gt;&lt;g id=&quot;g2&quot; transform=&quot;translate(-122.651,-2.2205581e-4)&quot;&gt;&lt;g id=&quot;g1&quot;&gt;&lt;path d=&quot;M 351.972,50.847 375.57,7.315 c 1.549,-2.882 0.998,-5.092 -1.658,-6.646 -2.883,-1.34 -5.098,-0.661 -6.646,1.989 l -23.928,43.88 c -21.055,-9.309 -43.324,-13.972 -66.807,-13.972 -23.488,0 -45.759,4.664 -66.806,13.972 L 185.796,2.658 c -1.555,-2.65 -3.77,-3.323 -6.646,-1.989 -2.662,1.561 -3.213,3.764 -1.658,6.646 l 23.599,43.532 c -23.929,12.203 -42.987,29.198 -57.167,51.022 -14.18,21.836 -21.273,45.698 -21.273,71.628 h 307.426 c 0,-25.924 -7.094,-49.787 -21.273,-71.628 C 394.623,80.045 375.675,63.05 351.972,50.847 Z m -136.433,63.318 c -2.552,2.558 -5.6,3.831 -9.143,3.831 -3.55,0 -6.536,-1.273 -8.972,-3.831 -2.436,-2.546 -3.654,-5.582 -3.654,-9.137 0,-3.543 1.218,-6.585 3.654,-9.137 2.436,-2.546 5.429,-3.819 8.972,-3.819 3.543,0 6.591,1.273 9.143,3.819 2.546,2.558 3.825,5.594 3.825,9.137 -0.007,3.549 -1.285,6.591 -3.825,9.137 z m 140.086,0 c -2.441,2.558 -5.434,3.831 -8.971,3.831 -3.551,0 -6.598,-1.273 -9.145,-3.831 -2.551,-2.546 -3.824,-5.582 -3.824,-9.137 0,-3.543 1.273,-6.585 3.824,-9.137 2.547,-2.546 5.594,-3.819 9.145,-3.819 3.543,0 6.529,1.273 8.971,3.819 2.438,2.558 3.654,5.594 3.654,9.137 0,3.549 -1.217,6.591 -3.654,9.137 z&quot; id=&quot;path1&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;,
&lt;span class=&quot;apps-inline&quot;&gt;&lt;i&gt;ClearSpace&lt;/i&gt;&lt;span class=&quot;app-inline&quot;&gt; &lt;a href=&quot;https://apps.apple.com/us/app/clearspace-reduce-screen-time/id1572515807&quot; target=&quot;_blank&quot;&gt;&lt;svg class=&quot;ios-icon&quot; xml:space=&quot;preserve&quot; width=&quot;17&quot; height=&quot;20&quot; version=&quot;1.1&quot; id=&quot;svg1&quot; viewBox=&quot;0 0 814.09996 999.89999&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;defs id=&quot;defs1&quot;&gt;&lt;/defs&gt;&lt;path d=&quot;m 788.1,340.9 c -5.8,4.5 -108.2,62.2 -108.2,190.5 0,148.4 130.3,200.9 134.2,202.2 -0.6,3.2 -20.7,71.9 -68.7,141.9 -42.8,61.6 -87.5,123.1 -155.5,123.1 -68,0 -85.5,-39.5 -164,-39.5 -76.5,0 -103.7,40.8 -165.9,40.8 -62.2,0 -105.6,-57 -155.5,-127 C 46.7,790.7 0,663 0,541.8 0,347.4 126.4,244.3 250.8,244.3 c 66.1,0 121.2,43.4 162.7,43.4 39.5,0 101.1,-46 176.3,-46 28.5,0 130.9,2.6 198.3,99.2 z M 554.1,159.4 C 585.2,122.5 607.2,71.3 607.2,20.1 607.2,13 606.6,5.8 605.3,0 554.7,1.9 494.5,33.7 458.2,75.8 c -28.5,32.4 -55.1,83.6 -55.1,135.5 0,7.8 1.3,15.6 1.9,18.1 3.2,0.6 8.4,1.3 13.6,1.3 45.4,0 102.5,-30.4 135.5,-71.3 z&quot; id=&quot;path1&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;,
&lt;span class=&quot;apps-inline&quot;&gt;&lt;i&gt;Refocus&lt;/i&gt;&lt;span class=&quot;app-inline&quot;&gt; &lt;a href=&quot;https://apps.apple.com/us/app/refocus-app-website-blocker/id1645639057&quot; target=&quot;_blank&quot;&gt;&lt;svg class=&quot;ios-icon&quot; xml:space=&quot;preserve&quot; width=&quot;17&quot; height=&quot;20&quot; version=&quot;1.1&quot; id=&quot;svg1&quot; viewBox=&quot;0 0 814.09996 999.89999&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;defs id=&quot;defs1&quot;&gt;&lt;/defs&gt;&lt;path d=&quot;m 788.1,340.9 c -5.8,4.5 -108.2,62.2 -108.2,190.5 0,148.4 130.3,200.9 134.2,202.2 -0.6,3.2 -20.7,71.9 -68.7,141.9 -42.8,61.6 -87.5,123.1 -155.5,123.1 -68,0 -85.5,-39.5 -164,-39.5 -76.5,0 -103.7,40.8 -165.9,40.8 -62.2,0 -105.6,-57 -155.5,-127 C 46.7,790.7 0,663 0,541.8 0,347.4 126.4,244.3 250.8,244.3 c 66.1,0 121.2,43.4 162.7,43.4 39.5,0 101.1,-46 176.3,-46 28.5,0 130.9,2.6 198.3,99.2 z M 554.1,159.4 C 585.2,122.5 607.2,71.3 607.2,20.1 607.2,13 606.6,5.8 605.3,0 554.7,1.9 494.5,33.7 458.2,75.8 c -28.5,32.4 -55.1,83.6 -55.1,135.5 0,7.8 1.3,15.6 1.9,18.1 3.2,0.6 8.4,1.3 13.6,1.3 45.4,0 102.5,-30.4 135.5,-71.3 z&quot; id=&quot;path1&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;,
&lt;span class=&quot;apps-inline&quot;&gt;&lt;i&gt;Opal&lt;/i&gt;&lt;span class=&quot;app-inline&quot;&gt; &lt;a href=&quot;https://apps.apple.com/us/app/opal-screen-time-for-focus/id1497465230&quot; target=&quot;_blank&quot;&gt;&lt;svg class=&quot;ios-icon&quot; xml:space=&quot;preserve&quot; width=&quot;17&quot; height=&quot;20&quot; version=&quot;1.1&quot; id=&quot;svg1&quot; viewBox=&quot;0 0 814.09996 999.89999&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;defs id=&quot;defs1&quot;&gt;&lt;/defs&gt;&lt;path d=&quot;m 788.1,340.9 c -5.8,4.5 -108.2,62.2 -108.2,190.5 0,148.4 130.3,200.9 134.2,202.2 -0.6,3.2 -20.7,71.9 -68.7,141.9 -42.8,61.6 -87.5,123.1 -155.5,123.1 -68,0 -85.5,-39.5 -164,-39.5 -76.5,0 -103.7,40.8 -165.9,40.8 -62.2,0 -105.6,-57 -155.5,-127 C 46.7,790.7 0,663 0,541.8 0,347.4 126.4,244.3 250.8,244.3 c 66.1,0 121.2,43.4 162.7,43.4 39.5,0 101.1,-46 176.3,-46 28.5,0 130.9,2.6 198.3,99.2 z M 554.1,159.4 C 585.2,122.5 607.2,71.3 607.2,20.1 607.2,13 606.6,5.8 605.3,0 554.7,1.9 494.5,33.7 458.2,75.8 c -28.5,32.4 -55.1,83.6 -55.1,135.5 0,7.8 1.3,15.6 1.9,18.1 3.2,0.6 8.4,1.3 13.6,1.3 45.4,0 102.5,-30.4 135.5,-71.3 z&quot; id=&quot;path1&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;,
&lt;span class=&quot;apps-inline&quot;&gt;&lt;i&gt;Jomo&lt;/i&gt;&lt;span class=&quot;app-inline&quot;&gt; &lt;a href=&quot;https://apps.apple.com/us/app/jomo-screen-time-blocker/id1609960918&quot; target=&quot;_blank&quot;&gt;&lt;svg class=&quot;ios-icon&quot; xml:space=&quot;preserve&quot; width=&quot;17&quot; height=&quot;20&quot; version=&quot;1.1&quot; id=&quot;svg1&quot; viewBox=&quot;0 0 814.09996 999.89999&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;defs id=&quot;defs1&quot;&gt;&lt;/defs&gt;&lt;path d=&quot;m 788.1,340.9 c -5.8,4.5 -108.2,62.2 -108.2,190.5 0,148.4 130.3,200.9 134.2,202.2 -0.6,3.2 -20.7,71.9 -68.7,141.9 -42.8,61.6 -87.5,123.1 -155.5,123.1 -68,0 -85.5,-39.5 -164,-39.5 -76.5,0 -103.7,40.8 -165.9,40.8 -62.2,0 -105.6,-57 -155.5,-127 C 46.7,790.7 0,663 0,541.8 0,347.4 126.4,244.3 250.8,244.3 c 66.1,0 121.2,43.4 162.7,43.4 39.5,0 101.1,-46 176.3,-46 28.5,0 130.9,2.6 198.3,99.2 z M 554.1,159.4 C 585.2,122.5 607.2,71.3 607.2,20.1 607.2,13 606.6,5.8 605.3,0 554.7,1.9 494.5,33.7 458.2,75.8 c -28.5,32.4 -55.1,83.6 -55.1,135.5 0,7.8 1.3,15.6 1.9,18.1 3.2,0.6 8.4,1.3 13.6,1.3 45.4,0 102.5,-30.4 135.5,-71.3 z&quot; id=&quot;path1&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;,
&lt;span class=&quot;apps-inline&quot;&gt;&lt;i&gt;SocialFocus: Hide Distractions&lt;/i&gt;&lt;/span&gt;,
&lt;span class=&quot;apps-inline&quot;&gt;&lt;i&gt;UnTrap for YouTube&lt;/i&gt;&lt;span class=&quot;app-inline&quot;&gt; &lt;a href=&quot;https://apps.apple.com/us/app/untrap-for-youtube/id1637438059&quot; target=&quot;_blank&quot;&gt;&lt;svg class=&quot;ios-icon&quot; xml:space=&quot;preserve&quot; width=&quot;17&quot; height=&quot;20&quot; version=&quot;1.1&quot; id=&quot;svg1&quot; viewBox=&quot;0 0 814.09996 999.89999&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;defs id=&quot;defs1&quot;&gt;&lt;/defs&gt;&lt;path d=&quot;m 788.1,340.9 c -5.8,4.5 -108.2,62.2 -108.2,190.5 0,148.4 130.3,200.9 134.2,202.2 -0.6,3.2 -20.7,71.9 -68.7,141.9 -42.8,61.6 -87.5,123.1 -155.5,123.1 -68,0 -85.5,-39.5 -164,-39.5 -76.5,0 -103.7,40.8 -165.9,40.8 -62.2,0 -105.6,-57 -155.5,-127 C 46.7,790.7 0,663 0,541.8 0,347.4 126.4,244.3 250.8,244.3 c 66.1,0 121.2,43.4 162.7,43.4 39.5,0 101.1,-46 176.3,-46 28.5,0 130.9,2.6 198.3,99.2 z M 554.1,159.4 C 585.2,122.5 607.2,71.3 607.2,20.1 607.2,13 606.6,5.8 605.3,0 554.7,1.9 494.5,33.7 458.2,75.8 c -28.5,32.4 -55.1,83.6 -55.1,135.5 0,7.8 1.3,15.6 1.9,18.1 3.2,0.6 8.4,1.3 13.6,1.3 45.4,0 102.5,-30.4 135.5,-71.3 z&quot; id=&quot;path1&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;,
&lt;span class=&quot;apps-inline&quot;&gt;&lt;i&gt;BB - Screen Time &amp;amp; App Blocker&lt;/i&gt;&lt;span class=&quot;app-inline&quot;&gt; &lt;a href=&quot;https://apps.apple.com/us/app/bb-screen-time-app-blocker/id6443657745&quot; target=&quot;_blank&quot;&gt;&lt;svg class=&quot;ios-icon&quot; xml:space=&quot;preserve&quot; width=&quot;17&quot; height=&quot;20&quot; version=&quot;1.1&quot; id=&quot;svg1&quot; viewBox=&quot;0 0 814.09996 999.89999&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;defs id=&quot;defs1&quot;&gt;&lt;/defs&gt;&lt;path d=&quot;m 788.1,340.9 c -5.8,4.5 -108.2,62.2 -108.2,190.5 0,148.4 130.3,200.9 134.2,202.2 -0.6,3.2 -20.7,71.9 -68.7,141.9 -42.8,61.6 -87.5,123.1 -155.5,123.1 -68,0 -85.5,-39.5 -164,-39.5 -76.5,0 -103.7,40.8 -165.9,40.8 -62.2,0 -105.6,-57 -155.5,-127 C 46.7,790.7 0,663 0,541.8 0,347.4 126.4,244.3 250.8,244.3 c 66.1,0 121.2,43.4 162.7,43.4 39.5,0 101.1,-46 176.3,-46 28.5,0 130.9,2.6 198.3,99.2 z M 554.1,159.4 C 585.2,122.5 607.2,71.3 607.2,20.1 607.2,13 606.6,5.8 605.3,0 554.7,1.9 494.5,33.7 458.2,75.8 c -28.5,32.4 -55.1,83.6 -55.1,135.5 0,7.8 1.3,15.6 1.9,18.1 3.2,0.6 8.4,1.3 13.6,1.3 45.4,0 102.5,-30.4 135.5,-71.3 z&quot; id=&quot;path1&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref30&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn31&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://speedbumpapp.com/en/blog/social-media-timer/&quot;&gt;How to add a timer to social media&lt;/a&gt; (&lt;a href=&quot;https://speedbumpapp.com/es/blog/social-media-timer/&quot;&gt;Spanish&lt;/a&gt;, &lt;a href=&quot;https://speedbumpapp.com/de/blog/social-media-timer/&quot;&gt;German&lt;/a&gt;) &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref31&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn32&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Launchers:
&lt;span class=&quot;apps-inline&quot;&gt;&lt;i&gt;Olauncher&lt;/i&gt;&lt;span class=&quot;app-inline&quot;&gt; &lt;a href=&quot;https://play.google.com/store/apps/details?id=app.olauncher&quot; target=&quot;_blank&quot;&gt;&lt;svg class=&quot;android-icon&quot; fill=&quot;#a4c639&quot; version=&quot;1.1&quot; id=&quot;Capa_1&quot; width=&quot;24&quot; height=&quot;16&quot; viewBox=&quot;0 0 307.42599 173.49678&quot; xml:space=&quot;preserve&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;defs id=&quot;defs2&quot;&gt;&lt;/defs&gt;&lt;g id=&quot;g2&quot; transform=&quot;translate(-122.651,-2.2205581e-4)&quot;&gt;&lt;g id=&quot;g1&quot;&gt;&lt;path d=&quot;M 351.972,50.847 375.57,7.315 c 1.549,-2.882 0.998,-5.092 -1.658,-6.646 -2.883,-1.34 -5.098,-0.661 -6.646,1.989 l -23.928,43.88 c -21.055,-9.309 -43.324,-13.972 -66.807,-13.972 -23.488,0 -45.759,4.664 -66.806,13.972 L 185.796,2.658 c -1.555,-2.65 -3.77,-3.323 -6.646,-1.989 -2.662,1.561 -3.213,3.764 -1.658,6.646 l 23.599,43.532 c -23.929,12.203 -42.987,29.198 -57.167,51.022 -14.18,21.836 -21.273,45.698 -21.273,71.628 h 307.426 c 0,-25.924 -7.094,-49.787 -21.273,-71.628 C 394.623,80.045 375.675,63.05 351.972,50.847 Z m -136.433,63.318 c -2.552,2.558 -5.6,3.831 -9.143,3.831 -3.55,0 -6.536,-1.273 -8.972,-3.831 -2.436,-2.546 -3.654,-5.582 -3.654,-9.137 0,-3.543 1.218,-6.585 3.654,-9.137 2.436,-2.546 5.429,-3.819 8.972,-3.819 3.543,0 6.591,1.273 9.143,3.819 2.546,2.558 3.825,5.594 3.825,9.137 -0.007,3.549 -1.285,6.591 -3.825,9.137 z m 140.086,0 c -2.441,2.558 -5.434,3.831 -8.971,3.831 -3.551,0 -6.598,-1.273 -9.145,-3.831 -2.551,-2.546 -3.824,-5.582 -3.824,-9.137 0,-3.543 1.273,-6.585 3.824,-9.137 2.547,-2.546 5.594,-3.819 9.145,-3.819 3.543,0 6.529,1.273 8.971,3.819 2.438,2.558 3.654,5.594 3.654,9.137 0,3.549 -1.217,6.591 -3.654,9.137 z&quot; id=&quot;path1&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;,
&lt;span class=&quot;apps-inline&quot;&gt;&lt;i&gt;minimalist phone&lt;/i&gt;&lt;span class=&quot;app-inline&quot;&gt; &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.qqlabs.minimalistlauncher&quot; target=&quot;_blank&quot;&gt;&lt;svg class=&quot;android-icon&quot; fill=&quot;#a4c639&quot; version=&quot;1.1&quot; id=&quot;Capa_1&quot; width=&quot;24&quot; height=&quot;16&quot; viewBox=&quot;0 0 307.42599 173.49678&quot; xml:space=&quot;preserve&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;defs id=&quot;defs2&quot;&gt;&lt;/defs&gt;&lt;g id=&quot;g2&quot; transform=&quot;translate(-122.651,-2.2205581e-4)&quot;&gt;&lt;g id=&quot;g1&quot;&gt;&lt;path d=&quot;M 351.972,50.847 375.57,7.315 c 1.549,-2.882 0.998,-5.092 -1.658,-6.646 -2.883,-1.34 -5.098,-0.661 -6.646,1.989 l -23.928,43.88 c -21.055,-9.309 -43.324,-13.972 -66.807,-13.972 -23.488,0 -45.759,4.664 -66.806,13.972 L 185.796,2.658 c -1.555,-2.65 -3.77,-3.323 -6.646,-1.989 -2.662,1.561 -3.213,3.764 -1.658,6.646 l 23.599,43.532 c -23.929,12.203 -42.987,29.198 -57.167,51.022 -14.18,21.836 -21.273,45.698 -21.273,71.628 h 307.426 c 0,-25.924 -7.094,-49.787 -21.273,-71.628 C 394.623,80.045 375.675,63.05 351.972,50.847 Z m -136.433,63.318 c -2.552,2.558 -5.6,3.831 -9.143,3.831 -3.55,0 -6.536,-1.273 -8.972,-3.831 -2.436,-2.546 -3.654,-5.582 -3.654,-9.137 0,-3.543 1.218,-6.585 3.654,-9.137 2.436,-2.546 5.429,-3.819 8.972,-3.819 3.543,0 6.591,1.273 9.143,3.819 2.546,2.558 3.825,5.594 3.825,9.137 -0.007,3.549 -1.285,6.591 -3.825,9.137 z m 140.086,0 c -2.441,2.558 -5.434,3.831 -8.971,3.831 -3.551,0 -6.598,-1.273 -9.145,-3.831 -2.551,-2.546 -3.824,-5.582 -3.824,-9.137 0,-3.543 1.273,-6.585 3.824,-9.137 2.547,-2.546 5.594,-3.819 9.145,-3.819 3.543,0 6.529,1.273 8.971,3.819 2.438,2.558 3.654,5.594 3.654,9.137 0,3.549 -1.217,6.591 -3.654,9.137 z&quot; id=&quot;path1&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;,
&lt;span class=&quot;apps-inline&quot;&gt;&lt;i&gt;Indistract&lt;/i&gt;&lt;span class=&quot;app-inline&quot;&gt; &lt;a href=&quot;https://play.google.com/store/apps/details?id=com.indistractablelauncher.android&quot; target=&quot;_blank&quot;&gt;&lt;svg class=&quot;android-icon&quot; fill=&quot;#a4c639&quot; version=&quot;1.1&quot; id=&quot;Capa_1&quot; width=&quot;24&quot; height=&quot;16&quot; viewBox=&quot;0 0 307.42599 173.49678&quot; xml:space=&quot;preserve&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;defs id=&quot;defs2&quot;&gt;&lt;/defs&gt;&lt;g id=&quot;g2&quot; transform=&quot;translate(-122.651,-2.2205581e-4)&quot;&gt;&lt;g id=&quot;g1&quot;&gt;&lt;path d=&quot;M 351.972,50.847 375.57,7.315 c 1.549,-2.882 0.998,-5.092 -1.658,-6.646 -2.883,-1.34 -5.098,-0.661 -6.646,1.989 l -23.928,43.88 c -21.055,-9.309 -43.324,-13.972 -66.807,-13.972 -23.488,0 -45.759,4.664 -66.806,13.972 L 185.796,2.658 c -1.555,-2.65 -3.77,-3.323 -6.646,-1.989 -2.662,1.561 -3.213,3.764 -1.658,6.646 l 23.599,43.532 c -23.929,12.203 -42.987,29.198 -57.167,51.022 -14.18,21.836 -21.273,45.698 -21.273,71.628 h 307.426 c 0,-25.924 -7.094,-49.787 -21.273,-71.628 C 394.623,80.045 375.675,63.05 351.972,50.847 Z m -136.433,63.318 c -2.552,2.558 -5.6,3.831 -9.143,3.831 -3.55,0 -6.536,-1.273 -8.972,-3.831 -2.436,-2.546 -3.654,-5.582 -3.654,-9.137 0,-3.543 1.218,-6.585 3.654,-9.137 2.436,-2.546 5.429,-3.819 8.972,-3.819 3.543,0 6.591,1.273 9.143,3.819 2.546,2.558 3.825,5.594 3.825,9.137 -0.007,3.549 -1.285,6.591 -3.825,9.137 z m 140.086,0 c -2.441,2.558 -5.434,3.831 -8.971,3.831 -3.551,0 -6.598,-1.273 -9.145,-3.831 -2.551,-2.546 -3.824,-5.582 -3.824,-9.137 0,-3.543 1.273,-6.585 3.824,-9.137 2.547,-2.546 5.594,-3.819 9.145,-3.819 3.543,0 6.529,1.273 8.971,3.819 2.438,2.558 3.654,5.594 3.654,9.137 0,3.549 -1.217,6.591 -3.654,9.137 z&quot; id=&quot;path1&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;,
&lt;span class=&quot;apps-inline&quot;&gt;&lt;i&gt;Blank Spaces&lt;/i&gt;&lt;span class=&quot;app-inline&quot;&gt; &lt;a href=&quot;https://apps.apple.com/us/app/blank-spaces-app/id1570856853&quot; target=&quot;_blank&quot;&gt;&lt;svg class=&quot;ios-icon&quot; xml:space=&quot;preserve&quot; width=&quot;17&quot; height=&quot;20&quot; version=&quot;1.1&quot; id=&quot;svg1&quot; viewBox=&quot;0 0 814.09996 999.89999&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;defs id=&quot;defs1&quot;&gt;&lt;/defs&gt;&lt;path d=&quot;m 788.1,340.9 c -5.8,4.5 -108.2,62.2 -108.2,190.5 0,148.4 130.3,200.9 134.2,202.2 -0.6,3.2 -20.7,71.9 -68.7,141.9 -42.8,61.6 -87.5,123.1 -155.5,123.1 -68,0 -85.5,-39.5 -164,-39.5 -76.5,0 -103.7,40.8 -165.9,40.8 -62.2,0 -105.6,-57 -155.5,-127 C 46.7,790.7 0,663 0,541.8 0,347.4 126.4,244.3 250.8,244.3 c 66.1,0 121.2,43.4 162.7,43.4 39.5,0 101.1,-46 176.3,-46 28.5,0 130.9,2.6 198.3,99.2 z M 554.1,159.4 C 585.2,122.5 607.2,71.3 607.2,20.1 607.2,13 606.6,5.8 605.3,0 554.7,1.9 494.5,33.7 458.2,75.8 c -28.5,32.4 -55.1,83.6 -55.1,135.5 0,7.8 1.3,15.6 1.9,18.1 3.2,0.6 8.4,1.3 13.6,1.3 45.4,0 102.5,-30.4 135.5,-71.3 z&quot; id=&quot;path1&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref32&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn33&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Productivity timers:
&lt;span class=&quot;apps-inline&quot;&gt;&lt;i&gt;Forest&lt;/i&gt;&lt;span class=&quot;app-inline&quot;&gt; &lt;a href=&quot;https://play.google.com/store/apps/details?id=cc.forestapp&quot; target=&quot;_blank&quot;&gt;&lt;svg class=&quot;android-icon&quot; fill=&quot;#a4c639&quot; version=&quot;1.1&quot; id=&quot;Capa_1&quot; width=&quot;24&quot; height=&quot;16&quot; viewBox=&quot;0 0 307.42599 173.49678&quot; xml:space=&quot;preserve&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;defs id=&quot;defs2&quot;&gt;&lt;/defs&gt;&lt;g id=&quot;g2&quot; transform=&quot;translate(-122.651,-2.2205581e-4)&quot;&gt;&lt;g id=&quot;g1&quot;&gt;&lt;path d=&quot;M 351.972,50.847 375.57,7.315 c 1.549,-2.882 0.998,-5.092 -1.658,-6.646 -2.883,-1.34 -5.098,-0.661 -6.646,1.989 l -23.928,43.88 c -21.055,-9.309 -43.324,-13.972 -66.807,-13.972 -23.488,0 -45.759,4.664 -66.806,13.972 L 185.796,2.658 c -1.555,-2.65 -3.77,-3.323 -6.646,-1.989 -2.662,1.561 -3.213,3.764 -1.658,6.646 l 23.599,43.532 c -23.929,12.203 -42.987,29.198 -57.167,51.022 -14.18,21.836 -21.273,45.698 -21.273,71.628 h 307.426 c 0,-25.924 -7.094,-49.787 -21.273,-71.628 C 394.623,80.045 375.675,63.05 351.972,50.847 Z m -136.433,63.318 c -2.552,2.558 -5.6,3.831 -9.143,3.831 -3.55,0 -6.536,-1.273 -8.972,-3.831 -2.436,-2.546 -3.654,-5.582 -3.654,-9.137 0,-3.543 1.218,-6.585 3.654,-9.137 2.436,-2.546 5.429,-3.819 8.972,-3.819 3.543,0 6.591,1.273 9.143,3.819 2.546,2.558 3.825,5.594 3.825,9.137 -0.007,3.549 -1.285,6.591 -3.825,9.137 z m 140.086,0 c -2.441,2.558 -5.434,3.831 -8.971,3.831 -3.551,0 -6.598,-1.273 -9.145,-3.831 -2.551,-2.546 -3.824,-5.582 -3.824,-9.137 0,-3.543 1.273,-6.585 3.824,-9.137 2.547,-2.546 5.594,-3.819 9.145,-3.819 3.543,0 6.529,1.273 8.971,3.819 2.438,2.558 3.654,5.594 3.654,9.137 0,3.549 -1.217,6.591 -3.654,9.137 z&quot; id=&quot;path1&quot;&gt;&lt;/path&gt;&lt;/g&gt;&lt;/g&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;,
&lt;span class=&quot;apps-inline&quot;&gt;&lt;i&gt;Flora&lt;/i&gt;&lt;span class=&quot;app-inline&quot;&gt; &lt;a href=&quot;https://apps.apple.com/us/app/flora-green-focus/id1225155794&quot; target=&quot;_blank&quot;&gt;&lt;svg class=&quot;ios-icon&quot; xml:space=&quot;preserve&quot; width=&quot;17&quot; height=&quot;20&quot; version=&quot;1.1&quot; id=&quot;svg1&quot; viewBox=&quot;0 0 814.09996 999.89999&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;defs id=&quot;defs1&quot;&gt;&lt;/defs&gt;&lt;path d=&quot;m 788.1,340.9 c -5.8,4.5 -108.2,62.2 -108.2,190.5 0,148.4 130.3,200.9 134.2,202.2 -0.6,3.2 -20.7,71.9 -68.7,141.9 -42.8,61.6 -87.5,123.1 -155.5,123.1 -68,0 -85.5,-39.5 -164,-39.5 -76.5,0 -103.7,40.8 -165.9,40.8 -62.2,0 -105.6,-57 -155.5,-127 C 46.7,790.7 0,663 0,541.8 0,347.4 126.4,244.3 250.8,244.3 c 66.1,0 121.2,43.4 162.7,43.4 39.5,0 101.1,-46 176.3,-46 28.5,0 130.9,2.6 198.3,99.2 z M 554.1,159.4 C 585.2,122.5 607.2,71.3 607.2,20.1 607.2,13 606.6,5.8 605.3,0 554.7,1.9 494.5,33.7 458.2,75.8 c -28.5,32.4 -55.1,83.6 -55.1,135.5 0,7.8 1.3,15.6 1.9,18.1 3.2,0.6 8.4,1.3 13.6,1.3 45.4,0 102.5,-30.4 135.5,-71.3 z&quot; id=&quot;path1&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;,
&lt;span class=&quot;apps-inline&quot;&gt;&lt;i&gt;Plantie&lt;/i&gt;&lt;span class=&quot;app-inline&quot;&gt; &lt;a href=&quot;https://apps.apple.com/us/app/plantie-stay-focused/id1135988868&quot; target=&quot;_blank&quot;&gt;&lt;svg class=&quot;ios-icon&quot; xml:space=&quot;preserve&quot; width=&quot;17&quot; height=&quot;20&quot; version=&quot;1.1&quot; id=&quot;svg1&quot; viewBox=&quot;0 0 814.09996 999.89999&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt;&lt;defs id=&quot;defs1&quot;&gt;&lt;/defs&gt;&lt;path d=&quot;m 788.1,340.9 c -5.8,4.5 -108.2,62.2 -108.2,190.5 0,148.4 130.3,200.9 134.2,202.2 -0.6,3.2 -20.7,71.9 -68.7,141.9 -42.8,61.6 -87.5,123.1 -155.5,123.1 -68,0 -85.5,-39.5 -164,-39.5 -76.5,0 -103.7,40.8 -165.9,40.8 -62.2,0 -105.6,-57 -155.5,-127 C 46.7,790.7 0,663 0,541.8 0,347.4 126.4,244.3 250.8,244.3 c 66.1,0 121.2,43.4 162.7,43.4 39.5,0 101.1,-46 176.3,-46 28.5,0 130.9,2.6 198.3,99.2 z M 554.1,159.4 C 585.2,122.5 607.2,71.3 607.2,20.1 607.2,13 606.6,5.8 605.3,0 554.7,1.9 494.5,33.7 458.2,75.8 c -28.5,32.4 -55.1,83.6 -55.1,135.5 0,7.8 1.3,15.6 1.9,18.1 3.2,0.6 8.4,1.3 13.6,1.3 45.4,0 102.5,-30.4 135.5,-71.3 z&quot; id=&quot;path1&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&lt;/a&gt;&lt;/span&gt;&lt;/span&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref33&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn34&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.zdnet.com/article/daylight-debuts-worlds-first-blue-light-free-computer-with-a-120hz-livepaper-display/&quot;&gt; Daylight debuts world’s first ‘blue-light-free computer’ with a 120Hz LivePaper display — ZDNET&lt;/a&gt; (&lt;em&gt;Note: Daylight uses a mix between conventional electrophoretic e-paper and LCD. It feels slightly less like paper, but still improves refresh rate.&lt;/em&gt;) &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref34&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn35&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/Modos-Labs/Glider&quot;&gt;“Open-source Eink monitor with an emphasis on low latency” — GitHub Modos-Labs/Glider&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref35&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn36&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.theguardian.com/technology/2017/jun/20/turning-smartphone-greyscale-attention-distraction-colour&quot;&gt;Will turning your phone to greyscale really do wonders for your attention? — The Guardian&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref36&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn37&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.theverge.com/2024/4/10/24124411/kobo-libra-colour-clara-colour-e-reader-kindle-e-ink&quot;&gt;Kobo announces its first color e-readers — The Verge&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref37&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn38&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.reddit.com/r/eink/comments/10hl3bv/hisense_a9_1_week_review/&quot;&gt;Hisense A9 - 1 Week Review — r/eink&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref38&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn39&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://help.boox.com/hc/en-us/community/posts/15815361554068-Why-oh-why-no-SIM-card-mobile-data-support&quot;&gt;Why oh why no SIM-card / mobile data support? — Boox Forums&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref39&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn40&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Onyx_Boox#GPL_Compliance&quot;&gt;GPL Compliance, Onyx Boox — Wikipedia&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref40&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn41&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://mudita.com/community/blog/introducing-mudita-kompakt/&quot;&gt;First glimpse of Mudita Kompakt — Mudita&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref41&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn42&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.theverge.com/2024/5/23/24163225/daylight-dc1-tablet-livepaper&quot;&gt;The Daylight DC1 is a $729 attempt to build a calmer computer — The Verge&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref42&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn43&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://youtu.be/2Y1nogFltPY?t=2240&quot;&gt;Episode #234: Anjan Katta (Founder of Daylight Computer Co), by THE 2AM PODCAST — YouTube&lt;/a&gt; @ 37:20 &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref43&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn44&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.youtube.com/watch?v=31FrND2oqys&quot;&gt;Android in Monochrome? | Blloc Zero 18 - exclusive first look&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref44&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn45&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://discord.gg/NSJC3XcKaK&quot;&gt;Blloc’s Discord server&lt;/a&gt; (more information in the &lt;em&gt;announcements&lt;/em&gt; channel) &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref45&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn46&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.theverge.com/2019/4/19/18508418/yota-devices-bankrupt-yotaphone&quot;&gt;The company behind the dual-screen YotaPhone is bankrupt — The Verge&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref46&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn47&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.techradar.com/reviews/phones/mobile-phones/yotaphone-2-1228308/review&quot;&gt;Yotaphone 2 review — TechRadar&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref47&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn48&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.pcmag.com/news/dual-screen-yotaphone-launches-in-russia-europe&quot;&gt;Dual-Screen YotaPhone Launches in Russia, Europe — PCMag&lt;/a&gt; (&lt;em&gt;Release of first generation only in EU, Russia, and Middle East&lt;/em&gt;) &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref48&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn49&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.pcmag.com/news/dual-screened-yotaphone-2-launches-in-europe&quot;&gt;Dual-Screened YotaPhone 2 Launches in Europe — PCMag&lt;/a&gt; (&lt;em&gt;Release of second generation only in EU, Russia, and Middle East&lt;/em&gt;) &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref49&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn50&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.androidpolice.com/2015/07/31/supply-issues-force-cancellation-of-north-american-yotaphone-2-despite-successful-crowdfunding-campaign/&quot;&gt;Supply Issues Force Cancellation Of North American YotaPhone 2 Despite Successful Crowdfunding Campaign — Android Police&lt;/a&gt; (&lt;em&gt;Release failure of second generation in the US&lt;/em&gt;) &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref50&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn51&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.techradar.com/news/dual-screen-yotaphone-3-has-now-launched-and-its-as-odd-as-ever&quot;&gt;Dual-screen YotaPhone 3 is finally official and it’s just as kooky as the last two — TechRadar&lt;/a&gt; (&lt;em&gt;Release of third and last generation only in China&lt;/em&gt;) &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref51&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn52&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://killedbygoogle.com/&quot;&gt;Killed by Google&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/phone-intro/#fnref52&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</content>
  </entry>
  <entry>
    <title>The bane of my existence: Supporting both async and sync code in Rust</title>
    <link href="https://nullderef.com/blog/rust-async-sync/" />
    <updated>2024-01-14T00:00:00Z</updated>
    <id>https://nullderef.com/blog/rust-async-sync/</id>
    <content type="html">&lt;details class=&quot;toc&quot;&gt;
&lt;summary&gt;Table of Contents&lt;/summary&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-async-sync/#introduction&quot;&gt;Introduction &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-async-sync/#the-first-approaches&quot;&gt;The first approaches &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-async-sync/#good-ol-copy-pasting&quot;&gt;Good ol’ copy-pasting &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-async-sync/#calling-block-on&quot;&gt;Calling block_on &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-async-sync/#duplicating-the-crate&quot;&gt;Duplicating the crate &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-async-sync/#what-ended-up-working-the-maybe-async-crate&quot;&gt;What ended up “working”: the maybe_async crate &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-async-sync/#the-problem&quot;&gt;The problem &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-async-sync/#the-feature-resolver-v2&quot;&gt;The feature resolver v2 &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-async-sync/#other-fails&quot;&gt;Other fails &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-async-sync/#fixing-maybe-async&quot;&gt;Fixing maybe_async &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-async-sync/#official-support&quot;&gt;Official Support &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-async-sync/#conclusion&quot;&gt;Conclusion &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/details&gt;
&lt;p&gt;&lt;a name=&quot;_introduction&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;introduction&quot; tabindex=&quot;-1&quot;&gt;Introduction &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-async-sync/#introduction&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Imagine you want to create a new library in Rust. All it does is wrap up a public API that you need for something else, like &lt;a href=&quot;https://developer.spotify.com/documentation/web-api/&quot;&gt;the Spotify API&lt;/a&gt; or maybe a database like &lt;a href=&quot;https://www.arangodb.com/&quot;&gt;ArangoDB&lt;/a&gt;. It’s not rocket science, you aren’t inventing something new or dealing with complex algorithms, so you expect it to be relatively straightforward.&lt;/p&gt;
&lt;p&gt;You decide to implement the library with async. Most of the work in your library has to do with performing HTTP requests, which are mostly I/O, so it makes sense (that, and because it’s what the cool kids use in Rust nowadays). You start coding and have the v0.1.0 release ready in a few days. “Neat”, you say, as &lt;code&gt;cargo publish&lt;/code&gt; finishes successfully and uploads your work to &lt;a href=&quot;https://crates.io&quot;&gt;crates.io&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;A couple of days pass, and you get a new notification on GitHub. Someone opened an issue:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;How can I use this library synchronously?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;My project doesn’t use async because it’s overly complex for what I need. I wanted to try your new library, but I’m not sure how to do it easily. I would rather not fill my code with &lt;code&gt;block_on(endpoint())&lt;/code&gt;. I’ve seen crates like &lt;a href=&quot;https://crates.io/crates/reqwest&quot;&gt;&lt;code&gt;reqwest&lt;/code&gt;&lt;/a&gt; exporting a &lt;a href=&quot;https://docs.rs/reqwest/0.11.4/reqwest/blocking/index.html&quot;&gt;&lt;code&gt;blocking&lt;/code&gt; module&lt;/a&gt; with the exact same functionality, could you perhaps do that as well?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Low-level wise, that sounds like a very complicated task. Having a common interface for both async code — which requires a runtime like &lt;a href=&quot;https://crates.io/crates/tokio&quot;&gt;&lt;code&gt;tokio&lt;/code&gt;&lt;/a&gt;, awaiting futures, pinning, etc — and regular sync code? I mean, they asked nicely, so maybe we can try. After all, the only difference in the code would be the occurrences of the &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;await&lt;/code&gt; keywords because you aren’t doing anything fancy.&lt;/p&gt;
&lt;p&gt;Well, this is &lt;em&gt;more or less&lt;/em&gt; what happened with the crate &lt;a href=&quot;https://crates.io/crates/rspotify&quot;&gt;&lt;code&gt;rspotify&lt;/code&gt;&lt;/a&gt;, which I used to maintain along with its creator &lt;a href=&quot;https://github.com/ramsayleung/&quot;&gt;Ramsay&lt;/a&gt;. For those who don’t know, it’s a wrapper for the Spotify Web API. To clarify, I did get this working in the end, although not as cleanly as I was hoping; I’ll try to explain the situation in this new article of the &lt;a href=&quot;https://nullderef.com/series/rspotify&quot;&gt;Rspotify series&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_the_first_approaches&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-first-approaches&quot; tabindex=&quot;-1&quot;&gt;The first approaches &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-async-sync/#the-first-approaches&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;To give more context, here’s what Rspotify’s client looks like, roughly:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Spotify&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* ... */&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Spotify&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;some_endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; param&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SpotifyResult&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; params &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;param&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; param&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/some-endpoint&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; params&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Essentially, we would have to make &lt;code&gt;some_endpoint&lt;/code&gt; available for both asynchronous and blocking users. The important question here is, how do you do this once you have dozens of endpoints? And how can you make it easy to switch between async and sync for the user?&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_good_ol_copy_pasting&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;good-ol-copy-pasting&quot; tabindex=&quot;-1&quot;&gt;Good ol’ copy-pasting &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-async-sync/#good-ol-copy-pasting&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This is what was first implemented. It was quite simple and it worked. You just need to copy the regular client code into a new &lt;a href=&quot;https://github.com/ramsayleung/rspotify/tree/v0.9/src/blocking&quot;&gt;&lt;code&gt;blocking&lt;/code&gt; module in Rspotify&lt;/a&gt;. &lt;a href=&quot;https://docs.rs/reqwest&quot;&gt;&lt;code&gt;reqwest&lt;/code&gt;&lt;/a&gt; (our HTTP client) and &lt;a href=&quot;https://docs.rs/reqwest/latest/reqwest/blocking/index.html&quot;&gt;&lt;code&gt;reqwest::blocking&lt;/code&gt;&lt;/a&gt; share the same interface, so we can manually remove keywords like &lt;code&gt;async&lt;/code&gt; or &lt;code&gt;.await&lt;/code&gt; and import &lt;code&gt;reqwest::blocking&lt;/code&gt; instead of &lt;code&gt;reqwest&lt;/code&gt; in the new module.&lt;/p&gt;
&lt;p&gt;Then, the Rspotify user just can just use &lt;code&gt;rspotify::blocking::Client&lt;/code&gt; instead of &lt;code&gt;rspotify::Client&lt;/code&gt;, and voilà! Their code is now blocking. This will bloat the binary size for async-only users, so we can just feature-gate it under the name &lt;code&gt;blocking&lt;/code&gt; and done.&lt;/p&gt;
&lt;p&gt;The problem was much more clear later on, though. Half the crate’s code was duplicated. Adding a new endpoint or modifying it meant writing or removing everything twice.&lt;/p&gt;
&lt;p&gt;There is no way to make sure both implementations are equivalent unless you test absolutely everything. Which isn’t a bad idea either, but maybe you copy-pasted the tests wrong! How about that? The poor reviewer would have to read through the same code twice to make sure both sides look alright — which sounds incredibly prone to human errors.&lt;/p&gt;
&lt;p&gt;In our experience, it really slowed down the development of Rspotify, specially for new contributors who weren’t used to this whole ordeal. As a new excited maintainer of Rspotify, I began to &lt;a href=&quot;https://github.com/ramsayleung/rspotify/issues/112&quot;&gt;investigate other possible solutions&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_calling_block_on&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;calling-block-on&quot; tabindex=&quot;-1&quot;&gt;Calling &lt;code&gt;block_on&lt;/code&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-async-sync/#calling-block-on&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/ramsayleung/rspotify/pull/120&quot;&gt;The second approach&lt;/a&gt; consisted on implementing everything on the async side. Then, you just make wrappers for the blocking interface, which call &lt;a href=&quot;https://docs.rs/tokio/latest/tokio/runtime/struct.Runtime.html#method.block_on&quot;&gt;&lt;code&gt;block_on&lt;/code&gt;&lt;/a&gt; internally. &lt;code&gt;block_on&lt;/code&gt; will run the future until completion, basically making it synchronous. You still need to copy the method &lt;em&gt;definitions&lt;/em&gt;, but the implementation is written once only:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;mod&lt;/span&gt; &lt;span class=&quot;token module-declaration namespace&quot;&gt;blocking&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Spotify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Spotify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Spotify&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; param&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SpotifyResult&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            runtime&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;block_on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;move&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;param&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that in order to call &lt;code&gt;block_on&lt;/code&gt;, you first have to create some kind of runtime in the endpoint method. For example, with &lt;code&gt;tokio&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; runtime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;tokio&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;runtime&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Builder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;basic_scheduler&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;enable_all&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This raises the question: should we initialize the runtime in each call to the endpoint, or is there a way to share it? We could keep it as a global (&lt;em&gt;ewwww&lt;/em&gt;), or perhaps better, we can save the runtime in the &lt;code&gt;Spotify&lt;/code&gt; struct. But since it takes a &lt;em&gt;mutable&lt;/em&gt; reference to the runtime, you’d have to wrap it up with &lt;code&gt;Arc&amp;lt;Mutex&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt;, completely killing the concurrency in your client. The proper way to do this is with Tokio’s &lt;a href=&quot;https://docs.rs/tokio/latest/tokio/runtime/struct.Handle.html&quot;&gt;&lt;code&gt;Handle&lt;/code&gt;&lt;/a&gt;, which looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;tokio&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;runtime&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token macro property&quot;&gt;lazy_static!&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// You can also use `once_cell`&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;RT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Runtime&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Runtime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; param&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SpotifyResult&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;RT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;handle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;block_on&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;move&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;param&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While the handle does make our blocking client faster&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-async-sync/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;, there is an even more performant way to do it. This is what &lt;a href=&quot;https://crates.io/crates/reqwest&quot;&gt;&lt;code&gt;reqwest&lt;/code&gt;&lt;/a&gt; itself does, in case you’re interested. In short, it spawns a thread that calls &lt;code&gt;block_on&lt;/code&gt; waiting on a channel with jobs&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-async-sync/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-async-sync/#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Unfortunately, this solution still has quite the overhead. You pull in large dependencies like &lt;code&gt;futures&lt;/code&gt; or &lt;code&gt;tokio&lt;/code&gt;, and include them in your binary. All of that, in order to… actually end up writing blocking code. So not only is it a cost at runtime, but also at compile time. It just feels wrong to me.&lt;/p&gt;
&lt;p&gt;And you still have a good amount of duplicate code, even if it’s just definitions, which can sum up. &lt;a href=&quot;https://crates.io/crates/reqwest&quot;&gt;&lt;code&gt;reqwest&lt;/code&gt;&lt;/a&gt; is a huge project and can probably afford this for their &lt;code&gt;blocking&lt;/code&gt; module. But for a less popular crate like &lt;code&gt;rspotify&lt;/code&gt;, this is harder to pull off.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_duplicating_the_crate&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;duplicating-the-crate&quot; tabindex=&quot;-1&quot;&gt;Duplicating the crate &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-async-sync/#duplicating-the-crate&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Another possible way to fix this is, as the features docs suggest, creating separate crates. We’d have &lt;code&gt;rspotify-sync&lt;/code&gt; and &lt;code&gt;rspotify-async&lt;/code&gt;, and users would just pick whichever crate they want as a dependency, even both if they need to. The problem is — again — how exactly do we generate both versions of the crate? &lt;a href=&quot;https://github.com/ramsayleung/rspotify/pull/253&quot;&gt;I was unable to do this without copy-pasting the entire crate&lt;/a&gt;, even with Cargo tricks like two &lt;code&gt;Cargo.toml&lt;/code&gt; files, one for each crate (which was quite inconvenient anyway).&lt;/p&gt;
&lt;p&gt;With this idea we can’t even use procedural macros because you can’t just create a new crate within a macro. We could define a file format to write templates of Rust code in order to replace parts of the code like &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;.await&lt;/code&gt;. But that sounds completely out of scope.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_what_ended_up_working_the_maybe_async_crate&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;what-ended-up-working-the-maybe-async-crate&quot; tabindex=&quot;-1&quot;&gt;What ended up “working”: the &lt;code&gt;maybe_async&lt;/code&gt; crate &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-async-sync/#what-ended-up-working-the-maybe-async-crate&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/ramsayleung/rspotify/pull/129&quot;&gt;The third attempt&lt;/a&gt; is based on a crate called &lt;a href=&quot;https://crates.io/crates/maybe_async&quot;&gt;&lt;code&gt;maybe_async&lt;/code&gt;&lt;/a&gt;. I remember foolishly thinking it was the perfect solution back when I discovered it.&lt;/p&gt;
&lt;p&gt;Anyway, the idea is that with this crate you can automatically remove the &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;.await&lt;/code&gt; occurrences in your code with a procedural macro, essentially automating the copy-pasting approach. For example:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[maybe_async::maybe_async]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* stuff */&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Generates the following code:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[cfg(not(feature = &lt;span class=&quot;token string&quot;&gt;&quot;is_sync&quot;&lt;/span&gt;))]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* stuff */&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token attribute attr-name&quot;&gt;#[cfg(feature = &lt;span class=&quot;token string&quot;&gt;&quot;is_sync&quot;&lt;/span&gt;)]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* stuff with `.await` removed */&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can configure whether you want asynchronous or blocking code by toggling the &lt;code&gt;maybe_async/is_sync&lt;/code&gt; feature when compiling the crate. The macro works for functions, traits and &lt;code&gt;impl&lt;/code&gt; blocks. If one conversion isn’t as easy as removing &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;.await&lt;/code&gt;, you can specify custom implementations with the &lt;code&gt;async_impl&lt;/code&gt; and &lt;code&gt;sync_impl&lt;/code&gt; procedural macros. It does this wonderfully, and we’ve already been using it for Rspotify for a while now.&lt;/p&gt;
&lt;p&gt;In fact, it worked so well that I made Rspotify &lt;em&gt;http-client agnostic&lt;/em&gt;, which is even more flexible than being &lt;em&gt;async/sync agnostic&lt;/em&gt;. This allows us to support multiple HTTP clients like &lt;a href=&quot;https://crates.io/crates/reqwest&quot;&gt;&lt;code&gt;reqwest&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://crates.io/crates/ureq&quot;&gt;&lt;code&gt;ureq&lt;/code&gt;&lt;/a&gt;, independently of whether the client is asynchronous or synchronous.&lt;/p&gt;
&lt;p&gt;Being &lt;em&gt;http-client agnostic&lt;/em&gt; is not that hard to implement if you have &lt;code&gt;maybe_async&lt;/code&gt; around. You just need to define a trait for the &lt;a href=&quot;https://github.com/ramsayleung/rspotify/blob/89b37219a2230cdcf08c4cfd2ebe46d64902f03d/rspotify-http/src/common.rs#L46&quot;&gt;HTTP client&lt;/a&gt;, and then implement it for each of the clients you want to support. A snippet of code is worth a thousand words (&lt;em&gt;you can find the full source for Rspotify’s &lt;a href=&quot;https://github.com/ramsayleung/rspotify/blob/master/rspotify-http/src/reqwest.rs#L97&quot;&gt;&lt;code&gt;reqwest&lt;/code&gt;’s client here&lt;/a&gt;, and &lt;a href=&quot;https://github.com/ramsayleung/rspotify/blob/master/rspotify-http/src/ureq.rs#L56&quot;&gt;&lt;code&gt;ureq&lt;/code&gt;’s here&lt;/a&gt;&lt;/em&gt;):&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[maybe_async]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;trait&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;HttpClient&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token attribute attr-name&quot;&gt;#[sync_impl]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpClient&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UreqClient&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;ureq&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* ... */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token attribute attr-name&quot;&gt;#[async_impl]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpClient&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReqwestClient&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;reqwest&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* ... */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;SpotifyClient&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpClient&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    http&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Http&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token attribute attr-name&quot;&gt;#[maybe_async]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpClient&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SpotifyClient&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Http&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token comment&quot;&gt;/* ... */&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, we could extend it so that whichever client they want to use can be enabled with feature flags in their &lt;code&gt;Cargo.toml&lt;/code&gt;. For example, if &lt;code&gt;client-ureq&lt;/code&gt; is enabled, since &lt;code&gt;ureq&lt;/code&gt; is synchronous, it would enable &lt;code&gt;maybe_async/is_sync&lt;/code&gt;. In turn, this would remove the &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;.await&lt;/code&gt; and the &lt;code&gt;#[async_impl]&lt;/code&gt; blocks, and the Rspotify client would use &lt;code&gt;ureq&lt;/code&gt;’s implementation internally.&lt;/p&gt;
&lt;p&gt;This solution has none of the downsides I listed in previous attempts:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;No code duplication at all&lt;/li&gt;
&lt;li&gt;No overhead neither at runtime nor at compile time. If the user wants a blocking client, they can use &lt;code&gt;ureq&lt;/code&gt;, which doesn’t pull &lt;code&gt;tokio&lt;/code&gt; and friends&lt;/li&gt;
&lt;li&gt;Quite easy to understand for the user; just configure a flag in you &lt;code&gt;Cargo.toml&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;However, stop reading for a couple of minutes and try to figure out why you shouldn’t do this. In fact, I’ll give you 9 months, which is how long it took me to do so…&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_the_problem&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;the-problem&quot; tabindex=&quot;-1&quot;&gt;The problem &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-async-sync/#the-problem&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/img/ZAe2iuAK29-1000.avif 1000w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/img/ZAe2iuAK29-1000.webp 1000w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://nullderef.com/img/ZAe2iuAK29-1000.jpeg&quot; alt=&quot;Picturing what this felt like&quot; width=&quot;1000&quot; height=&quot;567&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Well, the thing is that features in Rust must be &lt;strong&gt;additive&lt;/strong&gt;: “enabling a feature should not disable functionality, and it should usually be safe to enable any combination of features”. Cargo may merge features of a crate when it’s duplicated in the dependency tree in order to avoid compiling the same crate multiple times. &lt;a href=&quot;https://doc.rust-lang.org/cargo/reference/features.html#feature-unification&quot;&gt;The reference explains this quite well, if you want more details&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This optimization means that mutually exclusive features may break a dependency tree. In our case, &lt;code&gt;maybe_async/is_sync&lt;/code&gt; is a &lt;em&gt;toggle&lt;/em&gt; feature enabled by &lt;code&gt;client-ureq&lt;/code&gt;. So if you try to compile it with &lt;code&gt;client-reqwest&lt;/code&gt; also enabled, it will fail because &lt;code&gt;maybe_async&lt;/code&gt; will be configured to generate synchronous function signatures instead. It’s impossible to have a crate that depends on both sync and async Rspotify either directly or indirectly, and the whole concept of &lt;code&gt;maybe_async&lt;/code&gt; is currently wrong according to the Cargo reference.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_the_feature_resolver_v2&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;the-feature-resolver-v2&quot; tabindex=&quot;-1&quot;&gt;The feature resolver v2 &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-async-sync/#the-feature-resolver-v2&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;A common misconception is that this is fixed by the “feature resolver v2”, which &lt;a href=&quot;https://doc.rust-lang.org/cargo/reference/features.html#feature-resolver-version-2&quot;&gt;the reference also explains quite well&lt;/a&gt;. It has been enabled by default since the 2021 edition, but you can specify it inside your &lt;code&gt;Cargo.toml&lt;/code&gt; in previous ones. This new version, among other things, avoids unifying features in some special cases, but not in ours:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;Features enabled on platform-specific dependencies for targets not currently being built are ignored.&lt;/li&gt;
&lt;li&gt;Build-dependencies and proc-macros do not share features with normal dependencies.&lt;/li&gt;
&lt;li&gt;Dev-dependencies do not activate features unless building a target that needs them (like tests or examples).&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;Just in case, I tried to reproduce this myself, and it did work as I expected. &lt;a href=&quot;https://github.com/marioortizmanero/resolver-v2-conflict&quot;&gt;This repository&lt;/a&gt; is an example of conflicting features, which breaks with any feature resolver.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_other_fails&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;other-fails&quot; tabindex=&quot;-1&quot;&gt;Other fails &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-async-sync/#other-fails&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There were a few crates that also had this problem:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://crates.io/crates/arangors&quot;&gt;&lt;code&gt;arangors&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://crates.io/crates/aragog&quot;&gt;&lt;code&gt;aragog&lt;/code&gt;&lt;/a&gt;: wrappers for ArangoDB. Both use &lt;code&gt;maybe_async&lt;/code&gt; to switch between async and sync (&lt;code&gt;arangors&lt;/code&gt;’s author is the same person, in fact)&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-async-sync/#fn4&quot; id=&quot;fnref4&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-async-sync/#fn5&quot; id=&quot;fnref5&quot;&gt;[5]&lt;/a&gt;&lt;/sup&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://crates.io/crates/inkwell&quot;&gt;&lt;code&gt;inkwell&lt;/code&gt;&lt;/a&gt;: a wrapper for LLVM. It supports multiple versions of LLVM, which are not compatible with eachother&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-async-sync/#fn6&quot; id=&quot;fnref6&quot;&gt;[6]&lt;/a&gt;&lt;/sup&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://crates.io/crates/k8s-openapi&quot;&gt;&lt;code&gt;k8s-openapi&lt;/code&gt;&lt;/a&gt;: a wrapper for Kubernetes, with the same issue as &lt;code&gt;inkwell&lt;/code&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-async-sync/#fn7&quot; id=&quot;fnref7&quot;&gt;[7]&lt;/a&gt;&lt;/sup&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a name=&quot;_fixing_maybe_async&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;fixing-maybe-async&quot; tabindex=&quot;-1&quot;&gt;Fixing &lt;code&gt;maybe_async&lt;/code&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-async-sync/#fixing-maybe-async&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Once the crate started to gain popularity, this issue was opened in &lt;code&gt;maybe_async&lt;/code&gt;, which explains the situation and showcases a fix:&lt;/p&gt;
&lt;p style=&quot;text-align:center&quot;&gt;
  &lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&amp;nbsp;&lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/fMeow/maybe-async-rs/issues/6&quot;&gt;async and sync in the same program &lt;span class=&quot;gh-number&quot;&gt;fMeow/maybe-async-rs#6&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;&lt;code&gt;maybe_async&lt;/code&gt; would now have two feature flags: &lt;code&gt;is_sync&lt;/code&gt; and &lt;code&gt;is_async&lt;/code&gt;. The crate would generate the functions in the same way, but with a &lt;code&gt;_sync&lt;/code&gt; or &lt;code&gt;_async&lt;/code&gt; suffix appended to the identifier so that they wouldn’t be conflicting. For example:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[maybe_async::maybe_async]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* stuff */&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Would now generate the following code:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[cfg(feature = &lt;span class=&quot;token string&quot;&gt;&quot;is_async&quot;&lt;/span&gt;)]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endpoint_async&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* stuff */&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token attribute attr-name&quot;&gt;#[cfg(feature = &lt;span class=&quot;token string&quot;&gt;&quot;is_sync&quot;&lt;/span&gt;)]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endpoint_sync&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* stuff with `.await` removed */&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, these suffixes introduce noise, so I wondered if it would be possible to do it in a more ergonomic way. I forked &lt;code&gt;maybe_async&lt;/code&gt; and gave it a try, about which you can read more &lt;a href=&quot;https://github.com/fMeow/maybe-async-rs/issues/6#issuecomment-880581551&quot;&gt;in this series of comments&lt;/a&gt;. In summary, it was too complicated, and I ultimately gave up.&lt;/p&gt;
&lt;p&gt;The only way to fix this edge case would be to worsen the usability of Rspotify for everyone. But I’d argue that someone who depends on both async and sync is unlikely; we haven’t actually had anyone complaining yet. Unlike &lt;code&gt;reqwest&lt;/code&gt;, &lt;code&gt;rspotify&lt;/code&gt; is a “high level” library, so it’s hard to imagine a scenario where it appears more than once in a dependency tree in the first place.&lt;/p&gt;
&lt;p&gt;Perhaps we could ask the Cargo devs for help?&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_official_support&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;official-support&quot; tabindex=&quot;-1&quot;&gt;Official Support &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-async-sync/#official-support&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Rspotify is far from being the first who has been through this problem, so it might be interesting to read previous discussions about it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/rust-lang/rfcs/pull/2962&quot;&gt;This now-closed RFC for the Rust compiler&lt;/a&gt; suggested adding the &lt;code&gt;oneof&lt;/code&gt; configuration predicate (think &lt;code&gt;#[cfg(any(...))]&lt;/code&gt; and similars) to support exclusive features. This only makes it easier to have conflicting features for cases where there’s &lt;em&gt;no choice&lt;/em&gt;, but features should still be strictly additive.&lt;/li&gt;
&lt;li&gt;The previous RFC started &lt;a href=&quot;https://internals.rust-lang.org/t/pre-rfc-cargo-mutually-exclusive-features/13182/27&quot;&gt;some discussion&lt;/a&gt; in the context of allowing exclusive features in Cargo itself, and although it has some interesting info, it didn’t go too far.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/rust-lang/cargo/issues/2980&quot;&gt;This issue in Cargo&lt;/a&gt; explains a similar case with the Windows API. The discussion includes more examples and solution ideas, but none have made it to Cargo yet.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/rust-lang/cargo/issues/4803&quot;&gt;Another issue in Cargo&lt;/a&gt; asks for a way to test and build with combinations of flags easily. If features are strictly additive, then &lt;code&gt;cargo test --all-features&lt;/code&gt; will cover everything. But in case it doesn’t, the user has to run the command with multiple combinations of feature flags, which is quite cumbersome. This is already possible unofficially thanks to &lt;a href=&quot;https://github.com/taiki-e/cargo-hack&quot;&gt;&lt;code&gt;cargo-hack&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A completely different approach &lt;a href=&quot;https://blog.rust-lang.org/inside-rust/2023/02/23/keyword-generics-progress-report-feb-2023.html&quot;&gt;based on the Keyword Generics Initiative&lt;/a&gt;. It seems to be the most recent take on solving this, but it’s in an “exploration” phase, and &lt;a href=&quot;https://blog.rust-lang.org/inside-rust/2022/07/27/keyword-generics.html#q-is-there-an-rfc-available-to-read&quot;&gt;no RFCs are available as of this writing&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;According to &lt;a href=&quot;https://github.com/rust-lang/rfcs/pull/2962#issuecomment-664656377&quot;&gt;this old comment&lt;/a&gt;, it’s not something the Rust team has already discarded; it’s still being discussed.&lt;/p&gt;
&lt;p&gt;Although unofficial, another interesting approach that could be explored further in Rust is &lt;a href=&quot;https://sans-io.readthedocs.io/&quot;&gt;“Sans I/O”&lt;/a&gt;. This is a Python protocol that abstracts away the use of network protocols like HTTP in our case, thus maximizing reusability. An existing example in Rust would be &lt;a href=&quot;https://github.com/EmbarkStudios/tame-oidc&quot;&gt;&lt;code&gt;tame-oidc&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_conclusion&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-async-sync/#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We currently have a choice to make between:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;Ignoring the Cargo Reference. We could assume that noone is going to use both sync and async for Rspotify at the same time.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Fixing &lt;code&gt;maybe_async&lt;/code&gt; and adding &lt;code&gt;_async&lt;/code&gt; and &lt;code&gt;_sync&lt;/code&gt; suffixes to each endpoint in our library.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Dropping support for both async and sync code. It’s kind of become a mess that we don’t have the manpower to deal with and that &lt;a href=&quot;https://github.com/ramsayleung/rspotify/pull/224#issuecomment-909324671&quot;&gt;affects other parts of Rspotify&lt;/a&gt;. The problem is that some crates that depend on rspotify like &lt;a href=&quot;https://github.com/hrkfdn/ncspot&quot;&gt;&lt;code&gt;ncspot&lt;/code&gt;&lt;/a&gt; or &lt;a href=&quot;https://github.com/Spotifyd/spotifyd&quot;&gt;&lt;code&gt;spotifyd&lt;/code&gt;&lt;/a&gt; are blocking, and others like &lt;a href=&quot;https://github.com/Rigellute/spotify-tui&quot;&gt;&lt;code&gt;spotify-tui&lt;/code&gt;&lt;/a&gt; use async, so I’m not sure what they’d think.&lt;/p&gt;
&lt;p&gt;I know this is a problem that I’ve imposed to myself. We could just say “No. We only support async” or “No. We only support sync”. While there are users interested in being able to use both, sometimes you just have to say no. If such a feature becomes so complicated to deal with that your entire codebase becomes a mess, and you don’t have the engineering power to maintain it, then it’s your only choice. If someone cared enough, they could just fork the crate and convert it to synchronous for their own usage.&lt;/p&gt;
&lt;p&gt;After all, most API wrappers and the like only support either asynchronous or blocking code. &lt;a href=&quot;https://crates.io/crates/serenity&quot;&gt;&lt;code&gt;serenity&lt;/code&gt;&lt;/a&gt; (Discord API), &lt;a href=&quot;https://crates.io/crates/sqlx&quot;&gt;&lt;code&gt;sqlx&lt;/code&gt;&lt;/a&gt; (SQL toolkit) and &lt;a href=&quot;https://crates.io/crates/teloxide&quot;&gt;&lt;code&gt;teloxide&lt;/code&gt;&lt;/a&gt; (Telegram API) are async-only, for example, and they’re quite popular.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Even though it was quite frustrating at times, I don’t really regret spending so much time walking in circles trying to get both async and sync to work. I was contributing to Rspotify in the first place just to &lt;em&gt;learn&lt;/em&gt;. I had no deadlines and no stress, I just wanted to try to improve a library in Rust in my free time. And I &lt;em&gt;have&lt;/em&gt; learned a lot; hopefully you too, after reading this.&lt;/p&gt;
&lt;p&gt;Perhaps the lesson today is that we should remember that Rust is a low level language after all, and there are some things that aren’t possible without a lot of complexity. Anyhow, I’m looking forward to how the Rust team fixes this in the future.&lt;/p&gt;
&lt;p&gt;So what do you think? What would you do if you were a maintainer of Rspotify? You can leave a comment below if you like.&lt;/p&gt;

&lt;script src=&quot;https://f.convertkit.com/ckjs/ck.5.js&quot;&gt;&lt;/script&gt;
&lt;form action=&quot;https://app.kit.com/forms/7534343/subscriptions&quot; class=&quot;seva-form formkit-form&quot; method=&quot;post&quot; data-sv-form=&quot;7534343&quot; data-uid=&quot;3680381778&quot; data-format=&quot;inline&quot; data-version=&quot;5&quot; data-options=&#39;{&quot;settings&quot;:{&quot;after_subscribe&quot;:{&quot;action&quot;:&quot;message&quot;,&quot;success_message&quot;:&quot;Success! Check your email to confirm your subscription. If you can’t find it, look in the spam folder.&quot;,&quot;redirect_url&quot;:&quot;&quot;},&quot;analytics&quot;:{&quot;google&quot;:null,&quot;fathom&quot;:null,&quot;facebook&quot;:null,&quot;segment&quot;:null,&quot;pinterest&quot;:null,&quot;sparkloop&quot;:null,&quot;googletagmanager&quot;:null},&quot;modal&quot;:{&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;powered_by&quot;:{&quot;show&quot;:true,&quot;url&quot;:&quot;https://kit.com/features/forms?utm_campaign=poweredby&amp;amp;utm_content=form&amp;amp;utm_medium=referral&amp;amp;utm_source=dynamic&quot;},&quot;recaptcha&quot;:{&quot;enabled&quot;:false},&quot;return_visitor&quot;:{&quot;action&quot;:&quot;show&quot;,&quot;custom_content&quot;:&quot;&quot;},&quot;slide_in&quot;:{&quot;display_in&quot;:&quot;bottom_right&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;sticky_bar&quot;:{&quot;display_in&quot;:&quot;top&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15}},&quot;version&quot;:&quot;5&quot;}&#39; min-width=&quot;400 500 600 700 800&quot;&gt;
  &lt;div data-style=&quot;clean&quot;&gt;
    &lt;div class=&quot;formkit-header&quot; data-element=&quot;header&quot;&gt;
      &lt;h2&gt;Want to receive an email when I post?&lt;/h2&gt;
    &lt;/div&gt;
    &lt;ul class=&quot;formkit-alert formkit-alert-error&quot; data-element=&quot;errors&quot; data-group=&quot;alert&quot;&gt;&lt;/ul&gt;
    &lt;div data-element=&quot;fields&quot; data-stacked=&quot;false&quot; class=&quot;seva-fields formkit-fields&quot;&gt;
      &lt;div class=&quot;formkit-field&quot;&gt;
        &lt;input class=&quot;formkit-input&quot; name=&quot;email_address&quot; aria-label=&quot;Email Address&quot; placeholder=&quot;Email Address&quot; required=&quot;&quot; type=&quot;email&quot;&gt;
      &lt;/div&gt;
      &lt;button data-element=&quot;submit&quot; class=&quot;formkit-submit&quot; style=&quot;background-color:#e06c75;font-weight:700&quot;&gt;
        &lt;div class=&quot;formkit-spinner&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span class=&quot;&quot;&gt;Subscribe&lt;/span&gt;
      &lt;/button&gt;
    &lt;/div&gt;
    &lt;div class=&quot;formkit-guarantee&quot; data-element=&quot;guarantee&quot;&gt;
      &lt;p&gt;&lt;em&gt;Free and without spam. You can also use my &lt;a href=&quot;https://nullderef.com/index.xml&quot; target=&quot;_blank&quot;&gt;RSS&lt;/a&gt;, &lt;a href=&quot;https://x.com/null_deref&quot; target=&quot;_blank&quot;&gt;X&lt;/a&gt;, or &lt;a href=&quot;https://mstdn.social/@nullderef&quot; target=&quot;_blank&quot;&gt;Mastodon&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
      &lt;p style=&quot;opacity:.6;font-size:16px&quot;&gt;&lt;em&gt;Powered by &lt;a href=&quot;https://kit.com/&quot; target=&quot;_black&quot;&gt;Kit&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;style&gt;.formkit-form[data-uid=&quot;3680381778&quot;] a{text-decoration:underline}.formkit-form[data-uid=&quot;3680381778&quot;] legend{border:none;font-size:inherit;margin-bottom:10px;padding:0;position:relative;display:table}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-checkboxes,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-select{width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{border:0;border-radius:var(--radius);color:#fff;cursor:pointer;display:inline-block;text-align:center;font-weight:500;cursor:pointer;margin-bottom:15px;overflow:hidden;padding:0;position:relative;vertical-align:middle}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover{outline:0}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover&gt;span{background-color:rgba(0,0,0,.1)}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit&gt;span{display:block;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out;padding:12px 24px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input{background:#fff;padding:12px;border:1px solid var(--border);border-radius:var(--radius);-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;line-height:1.4;margin:0;-webkit-transition:border-color ease-out .3s;transition:border-color ease-out .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:focus{outline:0;border-color:#1677be;-webkit-transition:border-color ease .3s;transition:border-color ease .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-webkit-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-moz-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:-ms-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert{background:#f9fafb;border:1px solid #e3e3e3;border-radius:5px;-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;list-style:none;margin:25px auto;padding:12px;text-align:center;width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert:empty{display:none}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-success{background:#d3fbeb;border-color:#10bf7a;color:#0c905c}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-error{background:#fde8e2;border-color:#f2643b;color:#ea4110}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;height:0;width:0;margin:0 auto;position:absolute;top:0;left:0;right:0;width:0;overflow:hidden;text-align:center;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div{margin:auto;width:12px;height:12px;background-color:#fff;opacity:.3;border-radius:100%;display:inline-block;-webkit-animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both;animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:first-child{-webkit-animation-delay:-.32s;animation-delay:-.32s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:nth-child(2){-webkit-animation-delay:-.16s;animation-delay:-.16s}.formkit-form[data-uid=&quot;3680381778&quot;]{border:1px solid var(--border);border-radius:var(--radius);position:relative;overflow:hidden;background:var(--code-bg)}.formkit-form[data-uid=&quot;3680381778&quot;]{padding:20px;width:100%;position:relative}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-header{margin:0 0 27px 0;font-size:20px;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee{margin-top:10px;margin-bottom:32px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee&gt;p{margin:0;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-fields{display:flex;flex-wrap:wrap;margin:25px auto 0 auto;gap:10px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-field,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{flex:1 0 100%}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;]{padding:0 30px}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-field{flex:100 1 auto}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-submit{flex:1 1 auto}&lt;/style&gt;
&lt;/form&gt;
&lt;h2 id=&quot;footnotes&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 20 20&quot; version=&quot;1.1&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt; &lt;defs&gt;&lt;/defs&gt; &lt;path d=&quot;M 3.25,15 C 1.4550746,15 0,13.544925 0,11.75 V 3.25 C 0,1.4550746 1.4550746,0 3.25,0 h 13.5 C 18.544925,0 20,1.4550746 20,3.25 v 8.5 C 20,13.544925 18.544925,15 16.75,15 H 11.012485 L 5.9986815,18.750679 C 5.4458514,19.164165 4.6624979,19.051204 4.2490125,18.498373 4.0873576,18.282241 4,18.019591 4,17.749906 L 3.9992143,15 Z m 7.263515,-1.5 H 16.75 c 0.966498,0 1.75,-0.783502 1.75,-1.75 V 3.25 C 18.5,2.2835017 17.716498,1.5 16.75,1.5 H 3.25 C 2.2835017,1.5 1.5,2.2835017 1.5,3.25 v 8.5 c 0,0.966498 0.7835017,1.75 1.75,1.75 H 5.4987857 L 5.499,14.249786 5.4998574,17.25057 Z&quot;&gt;&lt;/path&gt; &lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/ramsayleung/rspotify/issues/112#issuecomment-683266508&quot;&gt;Cleaning up the &lt;code&gt;blocking&lt;/code&gt; module &lt;span class=&quot;gh-number&quot;&gt;ramsayleung/rspotify#112&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; &lt;a href=&quot;https://nullderef.com/blog/rust-async-sync/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/seanmonstar/reqwest/blob/0.10.x/src/blocking/client.rs#L757&quot;&gt;reqwest/src/blocking/client.rs @ line 757 — GitHub&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/rust-async-sync/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 20 20&quot; version=&quot;1.1&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; xmlns:svg=&quot;http://www.w3.org/2000/svg&quot;&gt; &lt;defs&gt;&lt;/defs&gt; &lt;path d=&quot;M 3.25,15 C 1.4550746,15 0,13.544925 0,11.75 V 3.25 C 0,1.4550746 1.4550746,0 3.25,0 h 13.5 C 18.544925,0 20,1.4550746 20,3.25 v 8.5 C 20,13.544925 18.544925,15 16.75,15 H 11.012485 L 5.9986815,18.750679 C 5.4458514,19.164165 4.6624979,19.051204 4.2490125,18.498373 4.0873576,18.282241 4,18.019591 4,17.749906 L 3.9992143,15 Z m 7.263515,-1.5 H 16.75 c 0.966498,0 1.75,-0.783502 1.75,-1.75 V 3.25 C 18.5,2.2835017 17.716498,1.5 16.75,1.5 H 3.25 C 2.2835017,1.5 1.5,2.2835017 1.5,3.25 v 8.5 c 0,0.966498 0.7835017,1.75 1.75,1.75 H 5.4987857 L 5.499,14.249786 5.4998574,17.25057 Z&quot;&gt;&lt;/path&gt; &lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/ramsayleung/rspotify/issues/112#issuecomment-683249563&quot;&gt;Cleaning up the &lt;code&gt;blocking&lt;/code&gt; module &lt;span class=&quot;gh-number&quot;&gt;ramsayleung/rspotify#112&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; &lt;a href=&quot;https://nullderef.com/blog/rust-async-sync/#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn4&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/fMeow/arangors/issues/37&quot;&gt;Proposal: Move &lt;code&gt;sync&lt;/code&gt; and &lt;code&gt;async&lt;/code&gt; features into seperate modules &lt;span class=&quot;gh-number&quot;&gt;fMeow/arangors#37&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; &lt;a href=&quot;https://nullderef.com/blog/rust-async-sync/#fnref4&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn5&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://gitlab.com/qonfucius/aragog/-/blob/0.140.0/src/lib.rs#L488&quot;&gt;aragog/src/lib.rs @ line 488 — GitLab&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/rust-async-sync/#fnref5&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn6&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/TheDan64/inkwell/blob/bfb0e32bc329fd35f6c5a529a1a6209936a147f8/src/lib.rs#L107&quot;&gt;inkwell/src/lib.rs @ line 107 — GitHub&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/rust-async-sync/#fnref6&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn7&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/Arnavion/k8s-openapi/blob/v0.13.0/build.rs#L31&quot;&gt;k8s-openapi/build.rs @ line 31 — GitHub&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/rust-async-sync/#fnref7&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</content>
  </entry>
  <entry>
    <title>[Talk] Rust, the best and worst thing to happen to Tremor</title>
    <link href="https://nullderef.com/blog/tremorcon22/" />
    <updated>2022-10-18T00:00:00Z</updated>
    <id>https://nullderef.com/blog/tremorcon22/</id>
    <content type="html">&lt;p&gt;Hello! I’ve recently had the pleasure of giving a talk at this year’s &lt;a href=&quot;https://community.cncf.io/events/details/cncf-tremor-community-presents-tremor-con-2022/&quot;&gt;TremorCon&lt;/a&gt;. I really enjoy public speaking, so this has been an excellent opportunity to get better at it (let me know your thoughts!). In case you missed it, you can check out the recording here:&lt;/p&gt;
&lt;iframe width=&quot;1600&quot; height=&quot;400&quot; src=&quot;https://www.youtube.com/embed/cz6nv5YzgqM&quot; title=&quot;Rust, the best and worst thing to happen to Tremor&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;
&lt;p&gt;You can also find the slides here in &lt;a href=&quot;https://nullderef.com/blog/tremorcon22/TremorCon22_MarioOrtizManero.pdf&quot;&gt;&lt;code&gt;pdf&lt;/code&gt;&lt;/a&gt; or in &lt;a href=&quot;https://nullderef.com/blog/tremorcon22/TremorCon22_MarioOrtizManero.pptx&quot;&gt;&lt;code&gt;pptx&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;If you want to learn about new events I’m involved in, like this one, you can follow me on &lt;a href=&quot;https://twitter.com/null_deref&quot;&gt;Twitter&lt;/a&gt; or &lt;a href=&quot;https://www.linkedin.com/in/marioortizmanero/&quot;&gt;LinkedIn&lt;/a&gt;. Don’t forget to also give &lt;a href=&quot;https://twitter.com/TremorDebs&quot;&gt;Tremor&lt;/a&gt; a follow, and to check out &lt;a href=&quot;https://www.youtube.com/watch?v=og3kIey0X5E&amp;amp;list=PLNTN4J6tdf22rEnqZlVRWHjiDyRBNy3Iw&quot;&gt;the rest of the conference&lt;/a&gt;!&lt;/p&gt;

&lt;script src=&quot;https://f.convertkit.com/ckjs/ck.5.js&quot;&gt;&lt;/script&gt;
&lt;form action=&quot;https://app.kit.com/forms/7534343/subscriptions&quot; class=&quot;seva-form formkit-form&quot; method=&quot;post&quot; data-sv-form=&quot;7534343&quot; data-uid=&quot;3680381778&quot; data-format=&quot;inline&quot; data-version=&quot;5&quot; data-options=&#39;{&quot;settings&quot;:{&quot;after_subscribe&quot;:{&quot;action&quot;:&quot;message&quot;,&quot;success_message&quot;:&quot;Success! Check your email to confirm your subscription. If you can’t find it, look in the spam folder.&quot;,&quot;redirect_url&quot;:&quot;&quot;},&quot;analytics&quot;:{&quot;google&quot;:null,&quot;fathom&quot;:null,&quot;facebook&quot;:null,&quot;segment&quot;:null,&quot;pinterest&quot;:null,&quot;sparkloop&quot;:null,&quot;googletagmanager&quot;:null},&quot;modal&quot;:{&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;powered_by&quot;:{&quot;show&quot;:true,&quot;url&quot;:&quot;https://kit.com/features/forms?utm_campaign=poweredby&amp;amp;utm_content=form&amp;amp;utm_medium=referral&amp;amp;utm_source=dynamic&quot;},&quot;recaptcha&quot;:{&quot;enabled&quot;:false},&quot;return_visitor&quot;:{&quot;action&quot;:&quot;show&quot;,&quot;custom_content&quot;:&quot;&quot;},&quot;slide_in&quot;:{&quot;display_in&quot;:&quot;bottom_right&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;sticky_bar&quot;:{&quot;display_in&quot;:&quot;top&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15}},&quot;version&quot;:&quot;5&quot;}&#39; min-width=&quot;400 500 600 700 800&quot;&gt;
  &lt;div data-style=&quot;clean&quot;&gt;
    &lt;div class=&quot;formkit-header&quot; data-element=&quot;header&quot;&gt;
      &lt;h2&gt;Want to receive an email when I post?&lt;/h2&gt;
    &lt;/div&gt;
    &lt;ul class=&quot;formkit-alert formkit-alert-error&quot; data-element=&quot;errors&quot; data-group=&quot;alert&quot;&gt;&lt;/ul&gt;
    &lt;div data-element=&quot;fields&quot; data-stacked=&quot;false&quot; class=&quot;seva-fields formkit-fields&quot;&gt;
      &lt;div class=&quot;formkit-field&quot;&gt;
        &lt;input class=&quot;formkit-input&quot; name=&quot;email_address&quot; aria-label=&quot;Email Address&quot; placeholder=&quot;Email Address&quot; required=&quot;&quot; type=&quot;email&quot;&gt;
      &lt;/div&gt;
      &lt;button data-element=&quot;submit&quot; class=&quot;formkit-submit&quot; style=&quot;background-color:#e06c75;font-weight:700&quot;&gt;
        &lt;div class=&quot;formkit-spinner&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span class=&quot;&quot;&gt;Subscribe&lt;/span&gt;
      &lt;/button&gt;
    &lt;/div&gt;
    &lt;div class=&quot;formkit-guarantee&quot; data-element=&quot;guarantee&quot;&gt;
      &lt;p&gt;&lt;em&gt;Free and without spam. You can also use my &lt;a href=&quot;https://nullderef.com/index.xml&quot; target=&quot;_blank&quot;&gt;RSS&lt;/a&gt;, &lt;a href=&quot;https://x.com/null_deref&quot; target=&quot;_blank&quot;&gt;X&lt;/a&gt;, or &lt;a href=&quot;https://mstdn.social/@nullderef&quot; target=&quot;_blank&quot;&gt;Mastodon&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
      &lt;p style=&quot;opacity:.6;font-size:16px&quot;&gt;&lt;em&gt;Powered by &lt;a href=&quot;https://kit.com/&quot; target=&quot;_black&quot;&gt;Kit&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;style&gt;.formkit-form[data-uid=&quot;3680381778&quot;] a{text-decoration:underline}.formkit-form[data-uid=&quot;3680381778&quot;] legend{border:none;font-size:inherit;margin-bottom:10px;padding:0;position:relative;display:table}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-checkboxes,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-select{width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{border:0;border-radius:var(--radius);color:#fff;cursor:pointer;display:inline-block;text-align:center;font-weight:500;cursor:pointer;margin-bottom:15px;overflow:hidden;padding:0;position:relative;vertical-align:middle}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover{outline:0}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover&gt;span{background-color:rgba(0,0,0,.1)}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit&gt;span{display:block;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out;padding:12px 24px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input{background:#fff;padding:12px;border:1px solid var(--border);border-radius:var(--radius);-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;line-height:1.4;margin:0;-webkit-transition:border-color ease-out .3s;transition:border-color ease-out .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:focus{outline:0;border-color:#1677be;-webkit-transition:border-color ease .3s;transition:border-color ease .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-webkit-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-moz-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:-ms-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert{background:#f9fafb;border:1px solid #e3e3e3;border-radius:5px;-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;list-style:none;margin:25px auto;padding:12px;text-align:center;width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert:empty{display:none}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-success{background:#d3fbeb;border-color:#10bf7a;color:#0c905c}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-error{background:#fde8e2;border-color:#f2643b;color:#ea4110}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;height:0;width:0;margin:0 auto;position:absolute;top:0;left:0;right:0;width:0;overflow:hidden;text-align:center;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div{margin:auto;width:12px;height:12px;background-color:#fff;opacity:.3;border-radius:100%;display:inline-block;-webkit-animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both;animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:first-child{-webkit-animation-delay:-.32s;animation-delay:-.32s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:nth-child(2){-webkit-animation-delay:-.16s;animation-delay:-.16s}.formkit-form[data-uid=&quot;3680381778&quot;]{border:1px solid var(--border);border-radius:var(--radius);position:relative;overflow:hidden;background:var(--code-bg)}.formkit-form[data-uid=&quot;3680381778&quot;]{padding:20px;width:100%;position:relative}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-header{margin:0 0 27px 0;font-size:20px;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee{margin-top:10px;margin-bottom:32px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee&gt;p{margin:0;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-fields{display:flex;flex-wrap:wrap;margin:25px auto 0 auto;gap:10px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-field,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{flex:1 0 100%}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;]{padding:0 30px}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-field{flex:100 1 auto}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-submit{flex:1 1 auto}&lt;/style&gt;
&lt;/form&gt;
</content>
  </entry>
  <entry>
    <title>Plugins in Rust: Wrapping Up</title>
    <link href="https://nullderef.com/blog/plugin-end/" />
    <updated>2022-07-26T00:00:00Z</updated>
    <id>https://nullderef.com/blog/plugin-end/</id>
    <content type="html">&lt;details class=&quot;toc&quot;&gt;
&lt;summary&gt;Table of Contents&lt;/summary&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#benchmarking&quot;&gt;Benchmarking &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#tooling&quot;&gt;Tooling &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#methodology&quot;&gt;Methodology &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#experiments&quot;&gt;Experiments &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#type-conversions&quot;&gt;Type conversions &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#double-box&quot;&gt;Double Box &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#hash-table-optimization&quot;&gt;Hash table optimization &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#abi-stable&quot;&gt;abi_stable &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#unimplemented-ideas&quot;&gt;Unimplemented ideas &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#more-detailed-benchmarking&quot;&gt;More detailed benchmarking &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#investigate-wrapper-overhead&quot;&gt;Investigate wrapper overhead &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#investigate-async-runtime-conflicts&quot;&gt;Investigate async runtime conflicts &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#benchmark-async-ffi&quot;&gt;Benchmark async_ffi &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#improve-error-reporting&quot;&gt;Improve error reporting &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#reduce-error-handling-boilerplate&quot;&gt;Reduce error handling boilerplate &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#reduce-async-boilerplate&quot;&gt;Reduce async boilerplate &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#improve-cross-platform-support&quot;&gt;Improve cross-platform support &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#performance-impact-of-panic-handling&quot;&gt;Performance impact of panic handling &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#use-z-randomize-layout-to-find-ffi-bugs&quot;&gt;Use -Z randomize-layout to find FFI bugs &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#try-raw-dynamic-loading&quot;&gt;Try raw dynamic loading &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#simplify-the-interface-further&quot;&gt;Simplify the interface further &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#for-the-long-term-future&quot;&gt;For the long-term future &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#conclusion&quot;&gt;Conclusion &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#appendix-a-open-source-contributions&quot;&gt;Appendix A: Open Source Contributions &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#external-contributions&quot;&gt;External Contributions &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#internal-contributions&quot;&gt;Internal Contributions &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#appendix-b-other-achievements&quot;&gt;Appendix B: Other Achievements &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#breaking-the-compiler&quot;&gt;Breaking the Compiler &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#lfx-mentorship-showcase&quot;&gt;LFX Mentorship Showcase &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#my-final-year-project&quot;&gt;My Final Year Project &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#kubecon-cloudnativecon-2022&quot;&gt;KubeCon + CloudNativeCon 2022 &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/details&gt;
&lt;p&gt;Welcome to the final article of this &lt;a href=&quot;https://nullderef.com/series/rust-plugins/&quot;&gt;series&lt;/a&gt;! Here I’ll showcase some clean-ups and optimizations I may or may not have performed yet, so that our plugin system can get closer to production. I will also run benchmarks for some of these to ensure that the changes are actually worth it.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_benchmarking&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;benchmarking&quot; tabindex=&quot;-1&quot;&gt;Benchmarking &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#benchmarking&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a name=&quot;_tooling&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;tooling&quot; tabindex=&quot;-1&quot;&gt;Tooling &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#tooling&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There are all kinds of ways to measure the performance of a system, and it’s important to take as many of them as possible into account. Here are the ones I used myself:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Raw metrics&lt;/strong&gt;: Tremor is able to measure its own raw throughput, latency ranges, or standard deviation. They serve as an overall score and are very easy to compare against eachother.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;code&gt;perf.data&lt;/code&gt;&lt;/strong&gt;: the command &lt;a href=&quot;https://perf.wiki.kernel.org/index.php/Main_Page&quot;&gt;&lt;code&gt;perf&lt;/code&gt;&lt;/a&gt; measures the runtime performance thanks to monitoring capabilities in the Linux kernel. It generates a file with the full execution trace, which can be visualized in multiple ways, including:
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://www.brendangregg.com/flamegraphs.html&quot;&gt;Flamegraphs&lt;/a&gt;: hierarchical and simple graphs.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;perf report&lt;/code&gt;: the integrated CLI, which is more detailed but harder to understand.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;High Dynamic Range (HDR) histograms&lt;/strong&gt;: Tremor’s latency percentiles can be turned into a graph with &lt;a href=&quot;https://hdrhistogram.github.io/HdrHistogram/plotFiles.html&quot;&gt;this site&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;There are many other techniques available for benchmarking, though. I wasn’t able to get into lower level performance indicators like cache misses. This part of the project has helped me realize how hard it is to benchmark a program reliably; I would suggest checking out other dedicated learning sources on the topic. You should also tune the process according to your use-case, since it may vary heavily between programs. See “&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#more-bench&quot;&gt;More detailed benchmarking&lt;/a&gt;” for more information.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_methodology&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;methodology&quot; tabindex=&quot;-1&quot;&gt;Methodology &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#methodology&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;My first issue was only having my laptop available for the benchmarks. It’s a decent machine, but it caused greater variance in the results, and it wasn’t very productive, since I had to stop everything I was doing to exclusively run them. The Tremor team helped me out by granting me access to their dedicated benchmarking server; here are the full specs of both machines, for reference:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Machine&lt;/th&gt;
&lt;th&gt;CPU&lt;/th&gt;
&lt;th&gt;RAM&lt;/th&gt;
&lt;th&gt;Disk&lt;/th&gt;
&lt;th&gt;Operating System&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Laptop (Dell Vostro 5481)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Intel i5-8265U&lt;/td&gt;
&lt;td&gt;16 GB @ 2667 MHz&lt;/td&gt;
&lt;td&gt;SSD&lt;/td&gt;
&lt;td&gt;Arch Linux, 5.18 kernel, 64 bits&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Benchmarking Machine&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Intel Xeon E-2278G&lt;/td&gt;
&lt;td&gt;32 GB @ 2667 MHz&lt;/td&gt;
&lt;td&gt;SSD&lt;/td&gt;
&lt;td&gt;Ubuntu 20.04, 5.4 kernel, 64 bits&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;I ended up developing &lt;a href=&quot;https://nullderef.com/blog/plugin-end/bench.sh&quot;&gt;a custom script&lt;/a&gt; to run all the tests properly:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It first validates the configuration, including what benchmarks to run and what binaries to compare. It was quite annoying to run into a typo 2 hours into the benchmarks…&lt;/li&gt;
&lt;li&gt;It performs a few warmup rounds before the actual benchmarks. This was specially important when using my laptop, since it started to overheat after the first few rounds, degrading its performance.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;One way to ensure the variance isn’t too high is to run the exact same benchmark twice. Here’s a histogram for both machines. Unexpectedly, the server ended up having greater variance with this test, but it was good enough to continue anyway.&lt;/p&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/img/taj1FfFDzc-1200.avif 1200w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/img/taj1FfFDzc-1200.webp 1200w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; alt=&quot;Histogram&quot; src=&quot;https://nullderef.com/img/taj1FfFDzc-1200.png&quot; width=&quot;1200&quot; height=&quot;330&quot;&gt;&lt;/picture&gt;
&lt;p&gt;I would strongly recommend anyone to get a separate machine to run benchmarks. Not only will they be more reliable, but also you will be able to continue working while the benchmarks run.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: The measurements in this article are performed with the new &lt;code&gt;#[repr(C)]&lt;/code&gt; interface, but the plugins aren’t actually loaded dynamically yet. This is because, as the &lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#_separating_runtime_and_interface&quot;&gt;planning&lt;/a&gt; specifies, we are on a previous step to actually making the plugins external. The final performance will be slightly worse.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_experiments&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;experiments&quot; tabindex=&quot;-1&quot;&gt;Experiments &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#experiments&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We can divide the experiments in two categories:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;em&gt;For tracking&lt;/em&gt;: the change is always going to be a good idea, because it may also clean up the code or make it more robust. We just measure its performance impact to report the results and avoid surprises.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;For investigation&lt;/em&gt;: the change increases the complexity to explicitly improve performance, so we need to make sure it’s worth our time. This improvement may occur directly with the change, or indirectly by helping to come up with new optimizations.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Benchmarking is only essential in the latter, but I thought it would be a great idea in both cases. That way, I can write up a report like this article, explaining the improvements I worked on.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_type_conversions&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;type-conversions&quot; tabindex=&quot;-1&quot;&gt;Type conversions &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#type-conversions&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The most important performance concern in the first version of the plugin system had to do with &lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#_reaching_reprc_blockers&quot;&gt;the issues I had found when trying to convert external types to the C ABI&lt;/a&gt;. In short, there was an existing type named &lt;code&gt;Value&lt;/code&gt;, which I needed to make FFI-safe for the plugin system. However, it was used throughout the entire codebase, and modifying its definition would’ve required a large amount of unrelated changes for just the first iteration.&lt;/p&gt;
&lt;p&gt;My &lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#_simplifying_the_type_at_the_ffi_boundary&quot;&gt;temporary fix&lt;/a&gt; was to create the copy &lt;code&gt;PdkValue&lt;/code&gt;, and convert to &lt;code&gt;Value&lt;/code&gt; and back when going through the FFI boundary. &lt;code&gt;PdkValue&lt;/code&gt; was only used for the plugin system, making it quite easy to implement in contrast.&lt;/p&gt;
&lt;p&gt;An obvious experiment was to fix this problem properly by making &lt;code&gt;Value&lt;/code&gt; FFI-safe and removing &lt;code&gt;PdkValue&lt;/code&gt;. Unless the performance degraded considerably, this was always going to be a nice change because it would simplify the code.&lt;/p&gt;
&lt;p&gt;After the fix, I was able to investigate about the impact of these conversions with some flamegraphs:&lt;/p&gt;
&lt;p&gt;With both &lt;code&gt;Value&lt;/code&gt; and &lt;code&gt;PdkValue&lt;/code&gt; (highlighted in pink are the occurrences of &lt;code&gt;From&lt;/code&gt;, &lt;code&gt;Into&lt;/code&gt;, and &lt;code&gt;FromIterator&lt;/code&gt;, which add up to 9.4% of all the calls in the execution):&lt;/p&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/img/1zVCvADAIa-1200.avif 1200w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/img/1zVCvADAIa-1200.webp 1200w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; alt=&quot;Flamegraph with Value and PdkValue&quot; src=&quot;https://nullderef.com/img/1zVCvADAIa-1200.png&quot; width=&quot;1200&quot; height=&quot;677&quot;&gt;&lt;/picture&gt;
&lt;p&gt;With a single &lt;code&gt;Value&lt;/code&gt; (the conversion calls add up to 5.2% now, and the highlighted parts are much more sparse and harder to notice):&lt;/p&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/img/IoPUeTX7m_-1200.avif 1200w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/img/IoPUeTX7m_-1200.webp 1200w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; alt=&quot;Flamegraph with a single Value&quot; src=&quot;https://nullderef.com/img/IoPUeTX7m_-1200.png&quot; width=&quot;1200&quot; height=&quot;674&quot;&gt;&lt;/picture&gt;
&lt;p&gt;The flamegraphs pointed out that Tremor was spending roughly 4.2% more of its execution time just converting between &lt;code&gt;Value&lt;/code&gt; and &lt;code&gt;PdkValue&lt;/code&gt;. This metric is tricky, though. Spending less time on type conversions doesn’t mean that the overall program is faster. The percentages are relative to the specific execution, and it’s possible that the rest of the program just got slower in the case of a single &lt;code&gt;Value&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;By checking other metrics we discover that this is more or less the case. The histogram shows that while a single &lt;code&gt;Value&lt;/code&gt; improves the latency, the overall throughput actually decreases. This compares a version without the plugin system (&lt;code&gt;connectors&lt;/code&gt; branch) vs. first version vs. single &lt;code&gt;Value&lt;/code&gt;.&lt;/p&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/img/Z-GLwCigi2-1200.avif 1200w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/img/Z-GLwCigi2-1200.webp 1200w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; alt=&quot;Histogram comparing plugin system versions&quot; src=&quot;https://nullderef.com/img/Z-GLwCigi2-1200.png&quot; width=&quot;1200&quot; height=&quot;332&quot;&gt;&lt;/picture&gt;
&lt;p&gt;Relative throughput comparison (higher is better):&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;text-align:center&quot;&gt;No plugin system (%)&lt;/th&gt;
&lt;th style=&quot;text-align:center&quot;&gt;With &lt;code&gt;PdkValue&lt;/code&gt; (%)&lt;/th&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Single &lt;code&gt;Value&lt;/code&gt; (%)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;100&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;66.67&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;63.74&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;This showcases one of my initial mistakes. My comparison only used the &lt;code&gt;passthrough&lt;/code&gt; benchmark, which simply forwards all input events to the output. For a more realistic use-case, I should’ve also tried with other configurations, which may include different payloads or event processing logic. In retrospect, these results should be taken with a grain of salt.&lt;/p&gt;
&lt;p&gt;Anyway, it all makes sense to some degree. Converting &lt;code&gt;Value&lt;/code&gt; to an FFI-safe type requires using, among others, &lt;code&gt;RHashMap&lt;/code&gt; instead of &lt;code&gt;HashMap&lt;/code&gt; internally. Tremor’s original &lt;code&gt;HashMap&lt;/code&gt; was taken from the &lt;a href=&quot;https://crates.io/crates/halfbrown&quot;&gt;&lt;code&gt;halfbrown&lt;/code&gt;&lt;/a&gt; crate, which was more performant than the standard library version, and even more than &lt;code&gt;abi_stable&lt;/code&gt;’s. Converting between types temporarily for the FFI interface might actually be faster than using less performant types everywhere.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_double_box&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;double-box&quot; tabindex=&quot;-1&quot;&gt;Double &lt;code&gt;Box&lt;/code&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#double-box&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I later came up with an improvement over the previous section. The &lt;code&gt;HashMap&lt;/code&gt; within &lt;code&gt;Value&lt;/code&gt; was originally stored inside a &lt;code&gt;Box&lt;/code&gt; to reduce the overall size of the enum. In the experiment, I just converted it to a &lt;code&gt;RBox&lt;/code&gt; and called it a day:&lt;/p&gt;
&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;enum Value {
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;    // ...
&lt;/span&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;    Object(Box&amp;lt;HashMap&amp;lt;String, Value&gt;&gt;),
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;    Object(RBox&amp;lt;RHashMap&amp;lt;RString, Value&gt;&gt;),
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;    // ...
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, turns out that unlike &lt;code&gt;RString&lt;/code&gt; or &lt;code&gt;RCow&lt;/code&gt;, &lt;code&gt;RHashMap&lt;/code&gt; isn’t a re-implementation of the underlying type. Writing a hash table from scratch is too complex, so the author just made it an opaque type that wraps the standard library version.&lt;/p&gt;
&lt;p&gt;Since &lt;code&gt;RHashMap&lt;/code&gt; is an opaque type, it was already on the heap internally thanks to another &lt;code&gt;RBox&lt;/code&gt;. Thus, the &lt;code&gt;Object&lt;/code&gt; variant was boxed twice, which is unnecessary, since the size of &lt;code&gt;RHashMap&lt;/code&gt; is already minimal. Having to allocate every hash table twice was probably costly, and fixing this counted as a clean-up, so it was worth trying.&lt;/p&gt;
&lt;p&gt;The subsequent benchmarks showed a slight improvement in performance, though I was still only using the &lt;code&gt;passthrough&lt;/code&gt; benchmark at this point. I kept the change because it didn’t make much sense to leave the doubly boxed hash table, and because it slightly improved the readability of the code. Here’s the histogram of a double box vs. a single box. Note that this histogram was recorded on the benchmarking server, while the previous one used my laptop. These tests aren’t meant to be compared between different sections.&lt;/p&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/img/eezPzOevaQ-1200.avif 1200w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/img/eezPzOevaQ-1200.webp 1200w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; alt=&quot;Histogram comparing box versions&quot; src=&quot;https://nullderef.com/img/eezPzOevaQ-1200.png&quot; width=&quot;1200&quot; height=&quot;332&quot;&gt;&lt;/picture&gt;
&lt;p&gt;Relative throughput comparison (higher is better):&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Double &lt;code&gt;RBox&lt;/code&gt; (%)&lt;/th&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Single &lt;code&gt;RBox&lt;/code&gt; (%)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;100&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;100.82&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;&lt;a name=&quot;_hash_table_optimization&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;hash-table-optimization&quot; tabindex=&quot;-1&quot;&gt;Hash table optimization &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#hash-table-optimization&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Using a single &lt;code&gt;Value&lt;/code&gt; improved the ergonomics of the code by sacrificing a bit of efficiency. We could try to fix this regression by creating an FFI-safe wrapper for the original external hash table implementation, which &lt;a href=&quot;https://github.com/rodrimati1992/abi_stable_crates/pull/83&quot;&gt;is what I did&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Tremor used &lt;code&gt;halfbrown&lt;/code&gt; both because it’s faster for their usecase, and because it has access to lower level functionality. The &lt;a href=&quot;https://github.com/rust-lang/rust/issues/56167&quot;&gt;&lt;code&gt;raw_entry&lt;/code&gt;&lt;/a&gt; interface is only available on nightly Rust, while &lt;code&gt;halfbrown&lt;/code&gt; exports it in its stable version. &lt;code&gt;raw_entry&lt;/code&gt; makes it possible to memoize hashes, enabling an optimization in Tremor’s JSON handling.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: This actually isn’t a great idea in the long term, because &lt;code&gt;raw_entry&lt;/code&gt; isn’t going to make it to a stable release&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;. The Rust team is working on a different interface, but it will probably be similar enough that updating our &lt;code&gt;raw_entry&lt;/code&gt; usage won’t be too much work.&lt;/em&gt;&lt;/p&gt;


















&lt;p&gt;This time I did it right since the beginning and tried multiple benchmarks. The results are much more varied now, and the efficiency can be analyzed from multiple points of view:&lt;/p&gt;
&lt;p&gt;Histogram for &lt;code&gt;passthrough&lt;/code&gt;, which simply redirects input events from a single source to the output:&lt;/p&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/img/UTAzov1I2c-1200.avif 1200w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/img/UTAzov1I2c-1200.webp 1200w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; alt=&quot;Histogram for passthrough&quot; src=&quot;https://nullderef.com/img/UTAzov1I2c-1200.png&quot; width=&quot;1200&quot; height=&quot;330&quot;&gt;&lt;/picture&gt;
&lt;p&gt;Histogram for &lt;code&gt;passthrough-two-inputs&lt;/code&gt;, which redirects input events from two different sources to the output:&lt;/p&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/img/3gybgOtbyd-1200.avif 1200w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/img/3gybgOtbyd-1200.webp 1200w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; alt=&quot;Histogram for passthrough-two-inputs&quot; src=&quot;https://nullderef.com/img/3gybgOtbyd-1200.png&quot; width=&quot;1200&quot; height=&quot;330&quot;&gt;&lt;/picture&gt;
&lt;p&gt;Histogram for &lt;code&gt;throughput-logging-json&lt;/code&gt;, which also implements some event processing logic over JSON data:&lt;/p&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/img/AZ90VDhm-c-1200.avif 1200w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/img/AZ90VDhm-c-1200.webp 1200w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; alt=&quot;Histogram for throughput-logging-json&quot; src=&quot;https://nullderef.com/img/AZ90VDhm-c-1200.png&quot; width=&quot;1200&quot; height=&quot;330&quot;&gt;&lt;/picture&gt;
&lt;p&gt;Histogram for &lt;code&gt;throughput-logging-msgpack&lt;/code&gt;, which also implements some event processing logic over &lt;a href=&quot;https://msgpack.org/index.html&quot;&gt;MessagePack&lt;/a&gt; data:&lt;/p&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/img/UpTzx6cL0y-1200.avif 1200w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/img/UpTzx6cL0y-1200.webp 1200w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; alt=&quot;Histogram for throughput-logging-msgpack&quot; src=&quot;https://nullderef.com/img/UpTzx6cL0y-1200.png&quot; width=&quot;1200&quot; height=&quot;330&quot;&gt;&lt;/picture&gt;
&lt;p&gt;Relative performance for number of events processed per second (higher is better):&lt;/p&gt;





&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;text-align:left&quot;&gt;Benchmark&lt;/th&gt;
&lt;th style=&quot;text-align:center&quot;&gt;No plugin system (%)&lt;/th&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Halfbrown, No JSON Known Key (%)&lt;/th&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Halfbrown (%)&lt;/th&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Hashbrown (%)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:left&quot;&gt;&lt;strong&gt;Passthrough&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;100&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;77.39&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;76.33&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;70.86&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:left&quot;&gt;&lt;strong&gt;Passthrough Two Inputs&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;100&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;70.34&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;69.78&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;68.76&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:left&quot;&gt;&lt;strong&gt;Throughput Logging JSON&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;100&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;65.57&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;64.48&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;69.21&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:left&quot;&gt;&lt;strong&gt;Throughput Logging MsgPack&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;100&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;66.66&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;65.52&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;70.69&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:left&quot;&gt;&lt;strong&gt;Average&lt;/strong&gt;&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;100&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;69.99&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;69.02&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;69.88&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;They seem to point out that, with the plugin system, &lt;a href=&quot;https://crates.io/crates/hashbrown&quot;&gt;&lt;code&gt;hashbrown&lt;/code&gt;&lt;/a&gt; (the standard hash table) might actually be better than &lt;code&gt;halfbrown&lt;/code&gt;. The JSON optimization I mentioned could possibly be worth removing now. But it’s really hard to make a decision just out of this set of benchmarks because of the complexity of the change.&lt;/p&gt;
&lt;p&gt;This time, the experiment is only done for investigation. These results must only be taken as an indicator of something worth investigating more in depth; it would be premature to make assumptions so early and remove &lt;code&gt;halfbrown&lt;/code&gt;. We would need to also analyze the impact of using &lt;code&gt;hashbrown&lt;/code&gt; without the plugin system, and understand &lt;em&gt;why this happens&lt;/em&gt;. For example, taking a look at the flamegraphs could help in figuring out the underlying reasoning.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_abi_stable&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;abi-stable&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;abi_stable&lt;/code&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#abi-stable&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;We can also take a look at the recurring occurrence of an experiment that isn’t worth pursuing. &lt;code&gt;abi_stable&lt;/code&gt; imposes all kinds of overheads, apart from the type validation step before loading a plugin. I wanted to learn more about the &lt;code&gt;Drop&lt;/code&gt; implementations, which require accessing to a vtable, and in cases like &lt;code&gt;RBox&lt;/code&gt;, dealing with additional logic to ensure everything is safe&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;After zooming into specific sections, we can see this overhead by ourselves in the call stack. Here’s the flamegraph with &lt;code&gt;abi_stable&lt;/code&gt;’s complex destructors, shown in pink and zoomed in:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/img/BGVwe4P8Fj-1200.avif 1200w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/img/BGVwe4P8Fj-1200.webp 1200w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; alt=&quot;Flamegraph zoomed in&quot; src=&quot;https://nullderef.com/img/BGVwe4P8Fj-1200.png&quot; width=&quot;1200&quot; height=&quot;343&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;However, I overestimated the importance of this problem. Taking a look at the big picture, it’s obvious that optimizing &lt;code&gt;RBox&lt;/code&gt;’s implementation of &lt;code&gt;Drop&lt;/code&gt; isn’t worth it yet. Here’s the flamegraph with &lt;code&gt;abi_stable&lt;/code&gt;’s complex destructors, shown in pink and without zooming in:&lt;/p&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/img/zpah3JXj6u-1200.avif 1200w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/img/zpah3JXj6u-1200.webp 1200w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; alt=&quot;Flamegraph without zoom&quot; src=&quot;https://nullderef.com/img/zpah3JXj6u-1200.png&quot; width=&quot;1200&quot; height=&quot;580&quot;&gt;&lt;/picture&gt;
&lt;p&gt;I’m definitely taking note of this idea in order to investigate about it in the future for further performance squeezing. But I currently have lots of other more impactful experiments in mind with a higher priority.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_unimplemented_ideas&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;unimplemented-ideas&quot; tabindex=&quot;-1&quot;&gt;Unimplemented ideas &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#unimplemented-ideas&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It’s already been a year since I started working with Tremor. I extended the mentorship from three months to twelve so that I could use it as my bachelor’s thesis (see “&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#thesis&quot;&gt;My Final Year Project&lt;/a&gt;”).&lt;/p&gt;
&lt;p&gt;But now that I have finally graduated and need to find a full-time job, I won’t be able to continue experimenting with the plugin system. My hope is that with the information in this series, the Tremor team can keep improving the plugin system so that it’s ready for deployment. The same applies to anyone reading this; hopefully you’ll have enough pointers to continue on your own after this.&lt;/p&gt;
&lt;p&gt;Here are some ideas worth considering that I haven’t been able to carry out:&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;more-bench&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;more-detailed-benchmarking&quot; tabindex=&quot;-1&quot;&gt;More detailed benchmarking &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#more-detailed-benchmarking&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;perf&lt;/code&gt; can track much more information than just the call stack. For example, &lt;code&gt;perf stat&lt;/code&gt; provides statistics like cache or branch misses&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;. Other profiling tools like &lt;a href=&quot;https://valgrind.org/&quot;&gt;Valgrind&lt;/a&gt;, &lt;a href=&quot;https://developer.amd.com/amd-uprof/&quot;&gt;AMD μProf&lt;/a&gt;, or &lt;a href=&quot;https://github.com/KDE/heaptrack&quot;&gt;Heaptrack&lt;/a&gt; may provide more specialized insights&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#fn4&quot; id=&quot;fnref4&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt;. The whole &lt;a href=&quot;https://nnethercote.github.io/perf-book/title-page.html&quot;&gt;The Rust Performance Book&lt;/a&gt; is a great read for more resources and ideas.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_investigate_wrapper_overhead&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;investigate-wrapper-overhead&quot; tabindex=&quot;-1&quot;&gt;Investigate wrapper overhead &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#investigate-wrapper-overhead&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The communication between runtime and plugins is handled through the C ABI at a lower level. To improve the ergonomics internally, the interface is accompanied by a set of wrappers, which convert the types to the standard library.&lt;/p&gt;
&lt;p&gt;Instead of returning a &lt;code&gt;Result&lt;/code&gt;, the following raw function uses the FFI-safe alternative &lt;code&gt;RResult&lt;/code&gt;, which doesn’t even work with &lt;code&gt;?&lt;/code&gt; for error handling. This example is quite specific to &lt;code&gt;abi_stable&lt;/code&gt;, but what’s important is that we just wrap the base function &lt;code&gt;connect&lt;/code&gt; and transform it to an easier to use one.&lt;/p&gt;
&lt;p&gt;Low-level interface to communicate between binaries:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[abi_stable::sabi_trait]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;trait&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;RawConnector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Send&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        ctx&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConnectorContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        attempt&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Attempt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BorrowingFfiFuture&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RResult&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/// Alias for the FFI-safe dynamic connector type&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;BoxedRawConnector&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RawConnector_TO&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;static&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RBox&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;High-level wrapper for what essentially is a &lt;code&gt;Box&amp;lt;dyn RawConnector&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;crate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Connector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BoxedRawConnector&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Connector&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute attr-name&quot;&gt;#[inline]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ctx&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ConnectorContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; attempt&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Attempt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;connect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; attempt&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map_err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Into&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;into&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// RBoxError -&gt; Error::PluginError&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;into&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// RResult -&gt; Result&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we just convert the return types, but we can do the same for the parameters or whatever boilerplate is needed for that function. Note that this wrapper only needs to be available in the runtime crate; the plugin is only meant to implement &lt;code&gt;RawConnector&lt;/code&gt; and doesn’t need access to &lt;code&gt;Connector&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The problem is that, even though wrappers reduce the much dreaded boilerplate, they can also affect performance-critical parts considerably. &lt;code&gt;connect&lt;/code&gt; is only called at the beginning of the program, but &lt;code&gt;on_event&lt;/code&gt; is invoked for every single event in the source, making it our &lt;em&gt;hot loop&lt;/em&gt;. Any small operation, like a type conversion, will have a much greater impact in there. This experiment consists on iteratively removing wrappers and looking for performance improvements.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_investigate_async_runtime_conflicts&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;investigate-async-runtime-conflicts&quot; tabindex=&quot;-1&quot;&gt;Investigate async runtime conflicts &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#investigate-async-runtime-conflicts&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As far as I understand, the runtime and the plugins don’t share the same async runtime state. Every binary has its own thread pool and works independently. It would be best to share everything between runtime and plugins, though it sounds incredibly complicated, and it would most likely require contributions to the async runtimes themselves.&lt;/p&gt;
&lt;p&gt;I also wonder what happens when the runtime and the plugin use different async runtimes, even if they are independent in the binaries. Tremor’s core is implemented with &lt;a href=&quot;https://crates.io/crates/async_std&quot;&gt;&lt;code&gt;async_std&lt;/code&gt;&lt;/a&gt;, but an external plugin could freely use &lt;a href=&quot;https://crates.io/crates/tokio&quot;&gt;&lt;code&gt;tokio&lt;/code&gt;&lt;/a&gt;, for example. I’ve heard that this could break in many ways, since async runtimes heavily rely on global state.&lt;/p&gt;
&lt;p&gt;Testing different async runtimes should be easy, so it should at least be done before reaching production just to document the behavior. What I’m not so sure about is how to avoid the conflict, in case it was problematic. The fix would probably be hacky, as this is a somewhat obscure problem.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_benchmark_async_ffi&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;benchmark-async-ffi&quot; tabindex=&quot;-1&quot;&gt;Benchmark &lt;code&gt;async_ffi&lt;/code&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#benchmark-async-ffi&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Checking how much of a performance impact &lt;a href=&quot;https://crates.io/crates/async_ffi&quot;&gt;&lt;code&gt;async_ffi&lt;/code&gt;&lt;/a&gt; causes sounds like a good idea. It’s actually a quite simple library; all it really does is implement opaque wrappers for the async-related types. But it’s used so often (once per call), that it may end up being noticeable.&lt;/p&gt;
&lt;p&gt;I’m sure that if &lt;code&gt;async_ffi&lt;/code&gt; ended up being an issue, it could be optimized internally in various ways. Furthermore, we currently use &lt;code&gt;async&lt;/code&gt; very liberally. Only using it when strictly necessary could also help reduce the overhead.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_improve_error_reporting&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;improve-error-reporting&quot; tabindex=&quot;-1&quot;&gt;Improve error reporting &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#improve-error-reporting&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For simplicity, errors in our interface are reported via &lt;code&gt;abi_stable&lt;/code&gt;’s &lt;a href=&quot;https://docs.rs/abi_stable/0.10.4/abi_stable/std_types/type.SendRBoxError.html&quot;&gt;&lt;code&gt;SendRBoxError&lt;/code&gt;&lt;/a&gt;, which is basically a &lt;code&gt;Box&amp;lt;dyn Error + Send&amp;gt;&lt;/code&gt;. It’s really the only way to do it, because a plugin could have any kind of error, and we can’t know them at compile-time.&lt;/p&gt;
&lt;p&gt;Using &lt;code&gt;Box&amp;lt;dyn Error&amp;gt;&lt;/code&gt; instead of a concrete type makes it really hard to identify and handle specific errors. We could implement our own &lt;code&gt;Error&lt;/code&gt; subtrait that provides more plugin-related information. An easier option is to define a more organized data structure with common kinds of errors. Here’s an example of a more organized error type, even if it still uses &lt;code&gt;Box&amp;lt;dyn Error&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;PluginError&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    err&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SendRBoxError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    kind&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PluginErrorKind&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;PluginErrorKind&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Internal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Implementation-defined errors we can&#39;t handle&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;ConnectionFailed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Edge cases in `connect`&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a name=&quot;_reduce_error_handling_boilerplate&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;reduce-error-handling-boilerplate&quot; tabindex=&quot;-1&quot;&gt;Reduce error handling boilerplate &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#reduce-error-handling-boilerplate&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In the plugin system we have to use an FFI-safe type like &lt;code&gt;RResult&lt;/code&gt; instead of &lt;code&gt;Result&lt;/code&gt;. Since the &lt;code&gt;Try&lt;/code&gt; trait isn’t stable&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#fn5&quot; id=&quot;fnref5&quot;&gt;[5]&lt;/a&gt;&lt;/sup&gt;, we can’t use the &lt;code&gt;?&lt;/code&gt; operator yet. &lt;code&gt;abi_stable&lt;/code&gt; exports the &lt;a href=&quot;https://docs.rs/abi_stable/0.10.4/abi_stable/macro.rtry.html&quot;&gt;&lt;code&gt;rtry&lt;/code&gt; macro&lt;/a&gt; as a substitute for &lt;code&gt;?&lt;/code&gt;, but in my experience it introduces noise in the code, making it uglier and harder to maintain.&lt;/p&gt;
&lt;p&gt;Depending on how much &lt;code&gt;Try&lt;/code&gt; is going to take to stabilize, creating a procedural macro might be worth our time. It would just replace &lt;code&gt;?&lt;/code&gt; with &lt;code&gt;rtry&lt;/code&gt; or whatever is configred, which should be somewhat simple to implement. I’m surprised I couldn’t find an existing crate for that. Readability would improve in longer functions with lots of error handling:&lt;/p&gt;
&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;#[try_with(rtry)]
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;fn fallible() -&gt; RResult&amp;lt;()&gt; {
&lt;/span&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;    if rtry!(fallible_op_1()) &amp;amp;&amp;amp; rtry!(fallible_op_2()) {
&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;        rtry!(fallible_op_3());
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;    if fallible_op_1()? &amp;amp;&amp;amp; fallible_op_2()? {
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;        fallible_op_3()?;
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;    }
&lt;/span&gt;
&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;    ROk(())
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;}
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Admittedly, the example above is an oversimplification. In reality, a single function may mix both &lt;code&gt;Result&lt;/code&gt; and &lt;code&gt;RResult&lt;/code&gt;, or require type conversions. The macro would probably end up being a bit more complex, but it might be worth considering regardless.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_reduce_async_boilerplate&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;reduce-async-boilerplate&quot; tabindex=&quot;-1&quot;&gt;Reduce async boilerplate &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#reduce-async-boilerplate&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;async_ffi&lt;/code&gt; would really benefit from a procedural macro as well. It always requires using &lt;code&gt;async move { /* ... */ }.into_ffi()&lt;/code&gt;, which is quite a bit of boilerplate and increases the indentation level by one.&lt;/p&gt;
&lt;p&gt;Without the procedural macro:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;async_ffi&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;FfiFuture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FutureExt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token attribute attr-name&quot;&gt;#[no_mangle]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;work&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arg&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FfiFuture&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;move&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; ret &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;do_some_io&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;do_some_sleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        ret
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;into_ffi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With the procedural macro, especially noticeable in more complex functions:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;async_ffi&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;async_ffi&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token attribute attr-name&quot;&gt;#[no_mangle]&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#[async_ffi]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;work&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arg&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; ret &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;do_some_io&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;do_some_sleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    ret
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I already opened an issue about this with more details for whoever wants to give it a try:&lt;/p&gt;
&lt;div style=&quot;text-align:center&quot;&gt;
  &lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&amp;nbsp;&lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/oxalica/async-ffi/issues/12&quot;&gt;Procedural macro for boilerplate &lt;span class=&quot;gh-number&quot;&gt;oxalica/async-ffi#12&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a name=&quot;_improve_cross_platform_support&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;improve-cross-platform-support&quot; tabindex=&quot;-1&quot;&gt;Improve cross-platform support &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#improve-cross-platform-support&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As I mentioned in earlier articles, our plugin system will only work on Windows, macOS, and Linux&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#fn6&quot; id=&quot;fnref6&quot;&gt;[6]&lt;/a&gt;&lt;/sup&gt;. It will still compile on other platforms, but possibly with data races in the dynamic linking internals.&lt;/p&gt;
&lt;p&gt;Specifically, &lt;a href=&quot;https://crates.io/crates/libloading&quot;&gt;&lt;code&gt;libloading&lt;/code&gt;&lt;/a&gt;, which is used by &lt;code&gt;abi_stable&lt;/code&gt;, states that its error handling isn’t fully thread-safe on some platforms&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#fn7&quot; id=&quot;fnref7&quot;&gt;[7]&lt;/a&gt;&lt;/sup&gt;, such as &lt;code&gt;dlerror&lt;/code&gt; on FreeBSD. Its only consequence should be garbage error messages, but I still wouldn’t risk it. There are two ways to approach this:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Quick fix&lt;/strong&gt;: add a compile-time error for any platform that isn’t explicitly supported. Before adding support for a new platform, we will have to manually ensure that its error handling for dynamic loading is thread-safe.&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Ideally, there should be a comment here with references that state the&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// thread-safety of the supported platforms.&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#[cfg(not(any(unix, windows, target_os = &lt;span class=&quot;token string&quot;&gt;&quot;macos&quot;&lt;/span&gt;)))]&lt;/span&gt;
&lt;span class=&quot;token macro property&quot;&gt;compile_error!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;This platform isn&#39;t currently supported. Please open a ticket on GitHub.&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Proper fix&lt;/strong&gt;: fix the data races upstream with an internal mutex. This is what the alternative &lt;a href=&quot;https://crates.io/crates/dlopen&quot;&gt;&lt;code&gt;dlopen&lt;/code&gt;&lt;/a&gt; does, though in their case they &lt;em&gt;always&lt;/em&gt; have the mutex, which is unnecessary in some platforms&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#fn8&quot; id=&quot;fnref8&quot;&gt;[8]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a name=&quot;_performance_impact_of_panic_handling&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;performance-impact-of-panic-handling&quot; tabindex=&quot;-1&quot;&gt;Performance impact of panic handling &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#performance-impact-of-panic-handling&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;abi_stable&lt;/code&gt; has to track all panic occurrences so that they don’t propagate through the FFI boundary. Otherwise, as discussed in other articles&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#fn9&quot; id=&quot;fnref9&quot;&gt;[9]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#fn10&quot; id=&quot;fnref10&quot;&gt;[10]&lt;/a&gt;&lt;/sup&gt;, we would be invoking undefined behaviour. &lt;code&gt;async_ffi&lt;/code&gt; also has special handling for panics, which overcomplicates the crate a bit.&lt;/p&gt;
&lt;p&gt;There are two interesting experiments to try:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Comparing the performance of &lt;code&gt;panic = &amp;quot;abort&amp;quot; and &lt;/code&gt;panic = “unwind”. Technically, aborting should optimize away most of the extra code&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#fn11&quot; id=&quot;fnref11&quot;&gt;[11]&lt;/a&gt;&lt;/sup&gt;, but it should only be used for research, as it would make debugging in production incredibly hard.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Preparing &lt;code&gt;abi_stable&lt;/code&gt; and &lt;code&gt;async_ffi&lt;/code&gt; for the stabilization of &lt;a href=&quot;https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html&quot;&gt;&lt;code&gt;extern &amp;quot;C-unwind&amp;quot;&lt;/code&gt;&lt;/a&gt;, thanks to which all the additional panic handling could be removed. Unlike aborting, we would still be able to debug errors just fine.&lt;/p&gt;
&lt;p&gt;For now, it’s only available on nightly without a certain release date, but it will surely be useful in the future, and we could measure how much panic handling costs. Testing new features like that also speeds up their release, since we could share feedback with the &lt;a href=&quot;https://github.com/rust-lang/project-ffi-unwind&quot;&gt;project group&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a name=&quot;_use_z_randomize_layout_to_find_ffi_bugs&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;use-z-randomize-layout-to-find-ffi-bugs&quot; tabindex=&quot;-1&quot;&gt;Use &lt;code&gt;-Z randomize-layout&lt;/code&gt; to find FFI bugs &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#use-z-randomize-layout-to-find-ffi-bugs&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The unstable compiler flag &lt;a href=&quot;https://github.com/rust-lang/compiler-team/issues/457&quot;&gt;&lt;code&gt;-Z randomize-layout&lt;/code&gt;&lt;/a&gt; randomizes the layout of &lt;code&gt;#[repr(Rust)]&lt;/code&gt; types. The Rust ABI is unstable: it explicitly doesn’t specify much about the type layouts, and we must not rely on them. However, in practice they’re usually consistent, at least within the same compiler version&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#fn12&quot; id=&quot;fnref12&quot;&gt;[12]&lt;/a&gt;&lt;/sup&gt;, so these errors can be hard to catch without a tool like this.&lt;/p&gt;
&lt;p&gt;We use &lt;code&gt;#[repr(C)]&lt;/code&gt; for the plugin system, so this flag shouldn’t cause any issues… unless we’re mistakenly interacting with the Rust ABI. In that case, the program would crash in random and unexpected ways, pointing out that there’s something wrong. It certainly won’t be a pleasant debugging experience, but it’s better than having it happen in production.&lt;/p&gt;
&lt;p&gt;This may even catch other bugs unrelated to the project. Tremor implements self-referential types and other optimizations, and it’s possible that some of them incorrectly rely on the Rust ABI.&lt;/p&gt;
&lt;p&gt;Tremor’s Continuous Integration tests could be run with &lt;code&gt;-Z randomize-layout&lt;/code&gt; to ensure that no opaque types export Rust types, even if &lt;code&gt;abi_stable&lt;/code&gt; prevents most cases within the plugin system.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_try_raw_dynamic_loading&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;try-raw-dynamic-loading&quot; tabindex=&quot;-1&quot;&gt;Try raw dynamic loading &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#try-raw-dynamic-loading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;What I didn’t know at the beginning of this journey is that the hardest part would be making everything &lt;code&gt;#[repr(C)]&lt;/code&gt;. Using &lt;a href=&quot;https://crates.io/crates/abi_stable&quot;&gt;&lt;code&gt;abi_stable&lt;/code&gt;&lt;/a&gt; is certainly very useful for types like &lt;code&gt;RVec&lt;/code&gt; and to create custom types, but at times I find the library too much.&lt;/p&gt;
&lt;p&gt;A few developers, including myself, think that it would be best to have separate libraries for all the utilities &lt;code&gt;abi_stable&lt;/code&gt; provides, rather than bundling everything in there. If they became modularized, making it the “community standard” would be easier, and we could have compatible alternatives for different preferences.&lt;/p&gt;
&lt;p&gt;It also boils down to just having more support from the community. &lt;code&gt;abi_stable&lt;/code&gt; is an incredibly complex library, and I want to give props to the legend &lt;a href=&quot;https://github.com/rodrimati1992&quot;&gt;rodrimati1992&lt;/a&gt; for creating and maintaining it. But such complexity makes it a scary crate to contribute to, which I know first-hand. Turning it into smaller crates would really help decentralize the work, in my opinion.&lt;/p&gt;
&lt;p&gt;Anyway, the point of this experiment is that &lt;code&gt;abi_stable&lt;/code&gt; isn’t a hard requirement for the plugin system. Using it definitely makes its implementation easier, but also introduces many overheads. Its main selling points are usability, safety, and backward compatibility, though with enough effort and care, we could write the plugin system by ourselves just with &lt;code&gt;libloading&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;It would be super useful to measure how much performance could be squeezed out by removing &lt;code&gt;abi_stable&lt;/code&gt;. This doesn’t need to be done over the full plugin system; we could benchmark smaller prototypes, like the ones I host at &lt;a href=&quot;https://github.com/marioortizmanero/pdk-experiments&quot;&gt;marioortizmanero/pdk-experiments&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_simplify_the_interface_further&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;simplify-the-interface-further&quot; tabindex=&quot;-1&quot;&gt;Simplify the interface further &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#simplify-the-interface-further&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I’m not fully satisfied with what the final interface for the plugin system looks like either. For the first version, all I did was change as little as possible so that we could have a working prototype. But there are many parts that could be simplified with dedicated refactors.&lt;/p&gt;
&lt;p&gt;Everything could be reorganized and cleaned up a bit, especially the plugin initialization and the repetitive opaque types. We should continue to minimize the use of complex communication patterns like channels. These improvements can be worked on iteratively, because you will only come up with new ideas over time.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_for_the_long_term_future&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;for-the-long-term-future&quot; tabindex=&quot;-1&quot;&gt;For the long-term future &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#for-the-long-term-future&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Our short-term goals focus on either usability or performance. However, there are more ways in which our plugin system could keep evolving afterwards:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Supporting more components&lt;/strong&gt; in the plugin system. We could keep splitting up the executables into even smaller pieces, continuing to improve the overall flexibility and compilation times.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Refining the user experience&lt;/strong&gt; by making the plugin loading smarter and easier to use. In the far future, we could even have a centralized repository with all the plugins, so that it’s as easy as possible to download and add them. You can think of &lt;a href=&quot;https://crates.io/&quot;&gt;crates.io&lt;/a&gt; or &lt;a href=&quot;https://hub.docker.com/&quot;&gt;hub.docker.com&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Improving the developer experience&lt;/strong&gt; is also essential. Anyone should be able to implement their own plugin, but ours is currently too tied to the internals of Tremor. We could create project templates, properly document everything, write examples, etc.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Security&lt;/strong&gt; is something we sacrificed when we chose dynamic loading as our base technology. We don’t have a sandbox, which would be essential if a plugin repository were to exist.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Restarting or unloading plugins&lt;/strong&gt; at runtime. Unloading is almost impossible, given that &lt;code&gt;abi_stable&lt;/code&gt; explicitly doesn’t support it&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#fn13&quot; id=&quot;fnref13&quot;&gt;[13]&lt;/a&gt;&lt;/sup&gt;. But restarting could work by simply stopping and leaking an existing plugin, and then loading it again with the same config. It would improve error resilience considerably, as we would be able to restart plugins upon failure (cautiously, or we could run out of memory after leaking too much).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a name=&quot;_conclusion&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;That was all! In summary, our new FFI interface initially slowed down the program by around 36%. In a few iterations, we’ve reduced that number to around 30%, and there are still many ideas left to try.&lt;/p&gt;
&lt;p&gt;The complexity of the whole project has turned out to be much greater than we predicted at the beginning. I still remember when we were unaware of the ABI instability and thought we could get away with an interface in pure Rust. Thus, I haven’t been able to attain some goals specified initially, and there’s still a bit of work left before reaching production, mainly related to performance.&lt;/p&gt;
&lt;p&gt;However, I’m sure my implementation will serve as a great base for what the plugin system may evolve into in the future! And hopefully, this series and my contributions (see “&lt;a href=&quot;https://nullderef.com/blog/plugin-end/#os&quot;&gt;Open Source Contributions&lt;/a&gt;”) will make plugin systems more accessible, even outside Tremor.&lt;/p&gt;
&lt;p&gt;Doing all of this in an open source environment has been enormously rewarding. Even if you’re working for a company with propietary software, please try to contribute upstream instead of forking or patching. Try to pay it back to those who save you from so much work, and submit a PR or an issue.&lt;/p&gt;
&lt;p&gt;This project has been possible thanks to Darach, Heinz, and Matthias, my mentors in the Tremor team ❤️. Thanks to Wayfair as well for supporting open source, and for funding this project. And one final thanks to the Rust community for motivating me to continue with the series, and for providing such splendid libraries and tools.&lt;/p&gt;
&lt;p&gt;Please &lt;a href=&quot;https://nullderef.com/blog/plugin-end/#comments&quot;&gt;leave a comment below&lt;/a&gt; for any questions or suggestions you may have. Also let me know of new advances on any of the ideas above, so that I can add a link for future readers. I would love to see your plugin system creations and results :)&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;os&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;appendix-a-open-source-contributions&quot; tabindex=&quot;-1&quot;&gt;Appendix A: Open Source Contributions &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#appendix-a-open-source-contributions&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;One of my favorite parts of the project has been contributing so much to all kinds of open source dependencies, so I’ve maintained a list of its occurrences. Some are more important than others, but it’s still a decent metric for my results. This skips the issues or pull requests that:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Contributed nothing (e.g., asking questions or discarded ideas).&lt;/li&gt;
&lt;li&gt;Were repetitive (e.g., I made a few identical PRs in Tremor when I was fixing problems with Git).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a name=&quot;_external_contributions&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;external-contributions&quot; tabindex=&quot;-1&quot;&gt;External Contributions &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#external-contributions&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;These include repositories not directly related to Tremor:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rust-lang/nomicon/issues/338&quot;&gt;Subtyping and Variance - Trait variance not covered &lt;span class=&quot;gh-number&quot;&gt;rust-lang/nomicon#338&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/szymonwieloch/rust-dlopen/issues/42&quot;&gt;&lt;code&gt;dlerror&lt;/code&gt; &lt;em&gt;is&lt;/em&gt; thread-safe on some platforms &lt;span class=&quot;gh-number&quot;&gt;szymonwieloch/rust-dlopen#42&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/wasmerio/wasmer/issues/2539&quot;&gt;Add deprecation notice to the crate &lt;code&gt;wasmer-runtime&lt;/code&gt; &lt;span class=&quot;gh-number&quot;&gt;wasmerio/wasmer#2539&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/oxalica/async-ffi/pull/10&quot;&gt;Support for &lt;code&gt;abi_stable&lt;/code&gt; &lt;span class=&quot;gh-number&quot;&gt;oxalica/async-ffi#10&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/oxalica/async-ffi/pull/11&quot;&gt;Cbindgen support &lt;span class=&quot;gh-number&quot;&gt;oxalica/async-ffi#11&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/oxalica/async-ffi/issues/12&quot;&gt;Procedural macro for boilerplate &lt;span class=&quot;gh-number&quot;&gt;oxalica/async-ffi#12&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rodrimati1992/abi_stable_crates/issues/52&quot;&gt;Generating C bindings &lt;span class=&quot;gh-number&quot;&gt;rodrimati1992/abi_stable_crates#52&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rodrimati1992/abi_stable_crates/issues/60&quot;&gt;Stable ABI for floating point numbers &lt;span class=&quot;gh-number&quot;&gt;rodrimati1992/abi_stable_crates#60&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rodrimati1992/abi_stable_crates/pull/55&quot;&gt;Fix ‘carte’ typo &lt;span class=&quot;gh-number&quot;&gt;rodrimati1992/abi_stable_crates#55&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rodrimati1992/abi_stable_crates/pull/57&quot;&gt;Fix some more typos &lt;span class=&quot;gh-number&quot;&gt;rodrimati1992/abi_stable_crates#57&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rodrimati1992/abi_stable_crates/pull/58&quot;&gt;Add support for .keys() and .values() in RHashMap &lt;span class=&quot;gh-number&quot;&gt;rodrimati1992/abi_stable_crates#58&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rodrimati1992/abi_stable_crates/pull/59&quot;&gt;Implement &lt;code&gt;Index&lt;/code&gt; for slices and vectors &lt;span class=&quot;gh-number&quot;&gt;rodrimati1992/abi_stable_crates#59&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rodrimati1992/abi_stable_crates/pull/61&quot;&gt;Support for &lt;code&gt;f32&lt;/code&gt; and &lt;code&gt;f64&lt;/code&gt; &lt;span class=&quot;gh-number&quot;&gt;rodrimati1992/abi_stable_crates#61&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rodrimati1992/abi_stable_crates/pull/68&quot;&gt;Implement &lt;code&gt;ROption::as_deref&lt;/code&gt; &lt;span class=&quot;gh-number&quot;&gt;rodrimati1992/abi_stable_crates#68&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rodrimati1992/abi_stable_crates/pull/70&quot;&gt;Implement &lt;code&gt;RVec::append&lt;/code&gt; &lt;span class=&quot;gh-number&quot;&gt;rodrimati1992/abi_stable_crates#70&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rodrimati1992/abi_stable_crates/pull/76&quot;&gt;Fix &lt;code&gt;R*&lt;/code&gt; lifetimes &lt;span class=&quot;gh-number&quot;&gt;rodrimati1992/abi_stable_crates#76&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rodrimati1992/abi_stable_crates/pull/77&quot;&gt;Fix inconsistencies with &lt;code&gt;RVec&lt;/code&gt; in respect to &lt;code&gt;Vec&lt;/code&gt; &lt;span class=&quot;gh-number&quot;&gt;rodrimati1992/abi_stable_crates#77&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rodrimati1992/abi_stable_crates/pull/82&quot;&gt;Implement &lt;code&gt;ROption::{ok_or,ok_or_else}&lt;/code&gt; &lt;span class=&quot;gh-number&quot;&gt;rodrimati1992/abi_stable_crates#82&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rodrimati1992/abi_stable_crates/pull/83&quot;&gt;&lt;code&gt;RHashMap::raw_entry[_mut]&lt;/code&gt; support &lt;span class=&quot;gh-number&quot;&gt;rodrimati1992/abi_stable_crates#83&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rodrimati1992/abi_stable_crates/pull/85&quot;&gt;Fix hasher &lt;span class=&quot;gh-number&quot;&gt;rodrimati1992/abi_stable_crates#85&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rodrimati1992/abi_stable_crates/pull/88&quot;&gt;Only implement &lt;code&gt;Default&lt;/code&gt; once &lt;span class=&quot;gh-number&quot;&gt;rodrimati1992/abi_stable_crates#88&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/simd-lite/simd-json-derive/pull/9&quot;&gt;Support for &lt;code&gt;abi_stable&lt;/code&gt; &lt;span class=&quot;gh-number&quot;&gt;simd-lite/simd-json-derive#9&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/simd-lite/simd-json-derive/issues/10&quot;&gt;No docs for v0.3.0 &lt;span class=&quot;gh-number&quot;&gt;simd-lite/simd-json-derive#10&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/simd-lite/value-trait/pull/14&quot;&gt;Add support for StableAbi &lt;span class=&quot;gh-number&quot;&gt;simd-lite/value-trait#14&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/simd-lite/value-trait/pull/16&quot;&gt;User friendliness for the win! (close #15) &lt;span class=&quot;gh-number&quot;&gt;simd-lite/value-trait#16&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/simd-lite/value-trait/pull/18&quot;&gt;Update abi_stable after upstreamed changes &lt;span class=&quot;gh-number&quot;&gt;simd-lite/value-trait#18&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/nagisa/rust_libloading/pull/94&quot;&gt;Small typo &lt;span class=&quot;gh-number&quot;&gt;nagisa/rust_libloading#94&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/szymonwieloch/rust-dlopen/pull/40&quot;&gt;Fix typo &lt;span class=&quot;gh-number&quot;&gt;szymonwieloch/rust-dlopen#40&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/Licenser/halfbrown/pull/13&quot;&gt;Implement &lt;code&gt;remove_entry&lt;/code&gt; &lt;span class=&quot;gh-number&quot;&gt;Licenser/halfbrown#13&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/Licenser/halfbrown/pull/14&quot;&gt;Implement &lt;code&gt;Clone&lt;/code&gt; and &lt;code&gt;Debug&lt;/code&gt; for &lt;code&gt;Iter&lt;/code&gt; &lt;span class=&quot;gh-number&quot;&gt;Licenser/halfbrown#14&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/Licenser/halfbrown/pull/16&quot;&gt;Relax constraints &lt;span class=&quot;gh-number&quot;&gt;Licenser/halfbrown#16&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/Licenser/halfbrown/pull/17&quot;&gt;Same &lt;code&gt;Default&lt;/code&gt; constraints &lt;span class=&quot;gh-number&quot;&gt;Licenser/halfbrown#17&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/Licenser/halfbrown/pull/18&quot;&gt;Fix &lt;code&gt;Clone&lt;/code&gt; requirements for &lt;code&gt;Iter&lt;/code&gt; &lt;span class=&quot;gh-number&quot;&gt;Licenser/halfbrown#18&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a name=&quot;_internal_contributions&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;internal-contributions&quot; tabindex=&quot;-1&quot;&gt;Internal Contributions &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#internal-contributions&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Here are the issues and pull requests created within Tremor’s repositories,
including those for the plugin system and other unrelated improvements:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/tremor-rs/tremor-runtime/pull/1434&quot;&gt;PDK support &lt;span class=&quot;gh-number&quot;&gt;tremor-rs/tremor-runtime#1434&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/marioortizmanero/tremor-runtime/pull/11&quot;&gt;PDK with a single value &lt;span class=&quot;gh-number&quot;&gt;marioortizmanero/tremor-runtime#11&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/tremor-rs/tremor-runtime/pull/1447&quot;&gt;Fix &lt;code&gt;makefile bench&lt;/code&gt; &lt;span class=&quot;gh-number&quot;&gt;tremor-rs/tremor-runtime#1447&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/marioortizmanero/tremor-runtime/pull/2&quot;&gt;Adding &lt;code&gt;abi_stable&lt;/code&gt; support for &lt;code&gt;tremor-script&lt;/code&gt; &lt;span class=&quot;gh-number&quot;&gt;marioortizmanero/tremor-runtime#2&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; (second attempt)&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/marioortizmanero/tremor-runtime/pull/1&quot;&gt;Adding &lt;code&gt;abi_stable&lt;/code&gt; support for &lt;code&gt;tremor-runtime&lt;/code&gt; &lt;span class=&quot;gh-number&quot;&gt;marioortizmanero/tremor-runtime#1&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; (second attempt)&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/tremor-rs/tremor-runtime/pull/1303&quot;&gt;Adding &lt;code&gt;abi_stable&lt;/code&gt; support for &lt;code&gt;tremor-value&lt;/code&gt; &lt;span class=&quot;gh-number&quot;&gt;tremor-rs/tremor-runtime#1303&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; (second attempt)&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/tremor-rs/tremor-runtime/pull/1287&quot;&gt;Plugin Development Kit: Connectors &lt;span class=&quot;gh-number&quot;&gt;tremor-rs/tremor-runtime#1287&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; (first attempt)&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/tremor-rs/tremor-runtime/issues/1353&quot;&gt;&lt;code&gt;deny&lt;/code&gt; statemements in &lt;code&gt;lib.rs&lt;/code&gt; should be enforced in the CI rather than in the code &lt;span class=&quot;gh-number&quot;&gt;tremor-rs/tremor-runtime#1353&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/tremor-rs/tremor-runtime/issues/1812&quot;&gt;&lt;code&gt;KnownKey&lt;/code&gt; relies on a deterministic hash builder &lt;span class=&quot;gh-number&quot;&gt;tremor-rs/tremor-runtime#1812&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/tremor-rs/tremor-www/pull/72&quot;&gt;Fix wrong links in getting started &lt;span class=&quot;gh-number&quot;&gt;tremor-rs/tremor-www#72&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/tremor-rs/tremor-www/issues/73&quot;&gt;Redirect &lt;code&gt;docs.tremor.rs&lt;/code&gt; to &lt;code&gt;www.tremor.rs/docs&lt;/code&gt; &lt;span class=&quot;gh-number&quot;&gt;tremor-rs/tremor-www#73&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/tremor-rs/tremor-www/pull/186&quot;&gt;Links pinned to 0.12 don’t work &lt;span class=&quot;gh-number&quot;&gt;tremor-rs/tremor-www#186&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/tremor-rs/tremor-www/pull/187&quot;&gt;Small fix in code snippet &lt;span class=&quot;gh-number&quot;&gt;tremor-rs/tremor-www#187&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/tremor-rs/tremor-www/issues/195&quot;&gt;No margins in benchmark page &lt;span class=&quot;gh-number&quot;&gt;tremor-rs/tremor-www#195&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;li&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/tremor-rs/tremor-www/pull/219&quot;&gt;Fix typos in benchmarks page &lt;span class=&quot;gh-number&quot;&gt;tremor-rs/tremor-www#219&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a name=&quot;_other_achievements&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;appendix-b-other-achievements&quot; tabindex=&quot;-1&quot;&gt;Appendix B: Other Achievements &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#appendix-b-other-achievements&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a name=&quot;_breaking_the_compiler&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;breaking-the-compiler&quot; tabindex=&quot;-1&quot;&gt;Breaking the Compiler &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#breaking-the-compiler&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I also managed to break the Rust compiler while working on this plugin system. It may not be as rare as one would think, but for some reason I felt oddly proud to achieve it, so I’ll share it here :)&lt;/p&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/img/wVJHaXpmcX-800.avif 800w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/img/wVJHaXpmcX-800.webp 800w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; alt=&quot;Rustc breakage&quot; src=&quot;https://nullderef.com/img/wVJHaXpmcX-800.png&quot; width=&quot;800&quot; height=&quot;351&quot;&gt;&lt;/picture&gt;
&lt;p&gt;It’s seemingly related to incremental compilation, and &lt;a href=&quot;https://github.com/rust-lang/rust/issues/90608&quot;&gt;someone had already reported it before&lt;/a&gt;. It should be fixed in a future version, and I haven’t come across it again.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_lfx_mentorship_showcase&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;lfx-mentorship-showcase&quot; tabindex=&quot;-1&quot;&gt;LFX Mentorship Showcase &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#lfx-mentorship-showcase&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I already shared this in a previous article, but for completeness I’ll repeat it here. This online event made it possible to showcase my work back in January with a &lt;a href=&quot;https://youtu.be/htLCyqY0kt0?t=3166&quot;&gt;quick 15-minute presentation&lt;/a&gt;. I couldn’t get into many technical details, but I’m sure it will be useful to anyone considering participating in a &lt;a href=&quot;https://lfx.linuxfoundation.org/tools/mentorship/&quot;&gt;LFX Mentorship&lt;/a&gt; or in &lt;a href=&quot;https://summerofcode.withgoogle.com/&quot;&gt;Google Summer of Code&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;thesis&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;my-final-year-project&quot; tabindex=&quot;-1&quot;&gt;My Final Year Project &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#my-final-year-project&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I have finally recently submitted this as &lt;a href=&quot;https://github.com/marioortizmanero/final-year-project&quot;&gt;my bachelor’s Final Year Project&lt;/a&gt;. This document takes a more academic approach, and I rigorously reorganized everything so that even developers unfamiliar with Rust can understand it. The abstract is in English, but unfortunately, the rest is in Spanish due to absurd university rules.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_kubecon_cloudnativecon_2022&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;kubecon-cloudnativecon-2022&quot; tabindex=&quot;-1&quot;&gt;KubeCon + CloudNativeCon 2022 &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-end/#kubecon-cloudnativecon-2022&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Thanks to the Tremor team, I was also able to presentially attend &lt;a href=&quot;https://events.linuxfoundation.org/kubecon-cloudnativecon-europe/&quot;&gt;KubeCon + CloudNativeCon&lt;/a&gt; 2022 in Valencia, Spain! It was my first conference and I was very pleasantly surprised by how nice everyone was. I had tons of fun and met smart folk with all kinds of backgrounds. If you’re on the fence about attending something similar, I strongly recommend you to go for it!&lt;/p&gt;
&lt;p&gt;Paella! &lt;a href=&quot;https://www.linkedin.com/feed/update/urn:li:share:6934450596049539072&quot;&gt;From my LinkedIn profile&lt;/a&gt;:&lt;/p&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/img/S2g45PcdMZ-500.avif 500w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/img/S2g45PcdMZ-500.webp 500w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://nullderef.com/img/S2g45PcdMZ-500.jpeg&quot; alt=&quot;Paella at KubeCon&quot; width=&quot;500&quot; height=&quot;602&quot;&gt;&lt;/picture&gt;

&lt;script src=&quot;https://f.convertkit.com/ckjs/ck.5.js&quot;&gt;&lt;/script&gt;
&lt;form action=&quot;https://app.kit.com/forms/7534343/subscriptions&quot; class=&quot;seva-form formkit-form&quot; method=&quot;post&quot; data-sv-form=&quot;7534343&quot; data-uid=&quot;3680381778&quot; data-format=&quot;inline&quot; data-version=&quot;5&quot; data-options=&#39;{&quot;settings&quot;:{&quot;after_subscribe&quot;:{&quot;action&quot;:&quot;message&quot;,&quot;success_message&quot;:&quot;Success! Check your email to confirm your subscription. If you can’t find it, look in the spam folder.&quot;,&quot;redirect_url&quot;:&quot;&quot;},&quot;analytics&quot;:{&quot;google&quot;:null,&quot;fathom&quot;:null,&quot;facebook&quot;:null,&quot;segment&quot;:null,&quot;pinterest&quot;:null,&quot;sparkloop&quot;:null,&quot;googletagmanager&quot;:null},&quot;modal&quot;:{&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;powered_by&quot;:{&quot;show&quot;:true,&quot;url&quot;:&quot;https://kit.com/features/forms?utm_campaign=poweredby&amp;amp;utm_content=form&amp;amp;utm_medium=referral&amp;amp;utm_source=dynamic&quot;},&quot;recaptcha&quot;:{&quot;enabled&quot;:false},&quot;return_visitor&quot;:{&quot;action&quot;:&quot;show&quot;,&quot;custom_content&quot;:&quot;&quot;},&quot;slide_in&quot;:{&quot;display_in&quot;:&quot;bottom_right&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;sticky_bar&quot;:{&quot;display_in&quot;:&quot;top&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15}},&quot;version&quot;:&quot;5&quot;}&#39; min-width=&quot;400 500 600 700 800&quot;&gt;
  &lt;div data-style=&quot;clean&quot;&gt;
    &lt;div class=&quot;formkit-header&quot; data-element=&quot;header&quot;&gt;
      &lt;h2&gt;Want to receive an email when I post?&lt;/h2&gt;
    &lt;/div&gt;
    &lt;ul class=&quot;formkit-alert formkit-alert-error&quot; data-element=&quot;errors&quot; data-group=&quot;alert&quot;&gt;&lt;/ul&gt;
    &lt;div data-element=&quot;fields&quot; data-stacked=&quot;false&quot; class=&quot;seva-fields formkit-fields&quot;&gt;
      &lt;div class=&quot;formkit-field&quot;&gt;
        &lt;input class=&quot;formkit-input&quot; name=&quot;email_address&quot; aria-label=&quot;Email Address&quot; placeholder=&quot;Email Address&quot; required=&quot;&quot; type=&quot;email&quot;&gt;
      &lt;/div&gt;
      &lt;button data-element=&quot;submit&quot; class=&quot;formkit-submit&quot; style=&quot;background-color:#e06c75;font-weight:700&quot;&gt;
        &lt;div class=&quot;formkit-spinner&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span class=&quot;&quot;&gt;Subscribe&lt;/span&gt;
      &lt;/button&gt;
    &lt;/div&gt;
    &lt;div class=&quot;formkit-guarantee&quot; data-element=&quot;guarantee&quot;&gt;
      &lt;p&gt;&lt;em&gt;Free and without spam. You can also use my &lt;a href=&quot;https://nullderef.com/index.xml&quot; target=&quot;_blank&quot;&gt;RSS&lt;/a&gt;, &lt;a href=&quot;https://x.com/null_deref&quot; target=&quot;_blank&quot;&gt;X&lt;/a&gt;, or &lt;a href=&quot;https://mstdn.social/@nullderef&quot; target=&quot;_blank&quot;&gt;Mastodon&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
      &lt;p style=&quot;opacity:.6;font-size:16px&quot;&gt;&lt;em&gt;Powered by &lt;a href=&quot;https://kit.com/&quot; target=&quot;_black&quot;&gt;Kit&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;style&gt;.formkit-form[data-uid=&quot;3680381778&quot;] a{text-decoration:underline}.formkit-form[data-uid=&quot;3680381778&quot;] legend{border:none;font-size:inherit;margin-bottom:10px;padding:0;position:relative;display:table}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-checkboxes,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-select{width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{border:0;border-radius:var(--radius);color:#fff;cursor:pointer;display:inline-block;text-align:center;font-weight:500;cursor:pointer;margin-bottom:15px;overflow:hidden;padding:0;position:relative;vertical-align:middle}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover{outline:0}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover&gt;span{background-color:rgba(0,0,0,.1)}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit&gt;span{display:block;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out;padding:12px 24px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input{background:#fff;padding:12px;border:1px solid var(--border);border-radius:var(--radius);-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;line-height:1.4;margin:0;-webkit-transition:border-color ease-out .3s;transition:border-color ease-out .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:focus{outline:0;border-color:#1677be;-webkit-transition:border-color ease .3s;transition:border-color ease .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-webkit-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-moz-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:-ms-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert{background:#f9fafb;border:1px solid #e3e3e3;border-radius:5px;-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;list-style:none;margin:25px auto;padding:12px;text-align:center;width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert:empty{display:none}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-success{background:#d3fbeb;border-color:#10bf7a;color:#0c905c}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-error{background:#fde8e2;border-color:#f2643b;color:#ea4110}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;height:0;width:0;margin:0 auto;position:absolute;top:0;left:0;right:0;width:0;overflow:hidden;text-align:center;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div{margin:auto;width:12px;height:12px;background-color:#fff;opacity:.3;border-radius:100%;display:inline-block;-webkit-animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both;animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:first-child{-webkit-animation-delay:-.32s;animation-delay:-.32s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:nth-child(2){-webkit-animation-delay:-.16s;animation-delay:-.16s}.formkit-form[data-uid=&quot;3680381778&quot;]{border:1px solid var(--border);border-radius:var(--radius);position:relative;overflow:hidden;background:var(--code-bg)}.formkit-form[data-uid=&quot;3680381778&quot;]{padding:20px;width:100%;position:relative}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-header{margin:0 0 27px 0;font-size:20px;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee{margin-top:10px;margin-bottom:32px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee&gt;p{margin:0;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-fields{display:flex;flex-wrap:wrap;margin:25px auto 0 auto;gap:10px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-field,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{flex:1 0 100%}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;]{padding:0 30px}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-field{flex:100 1 auto}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-submit{flex:1 1 auto}&lt;/style&gt;
&lt;/form&gt;
&lt;h2 id=&quot;footnotes&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/rust-lang/rust/issues/56167#issuecomment-810457405&quot;&gt;@Amanieu’s comment — Tracking issue for HashMap::raw_entry&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-end/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/rodrimati1992/abi_stable_crates/blob/4514c38829548ef6f4201dac183c1e866bdd2401/abi_stable/src/std_types/boxed.rs#L597&quot;&gt;&lt;code&gt;Drop&lt;/code&gt; for &lt;code&gt;RBox&lt;/code&gt; in &lt;code&gt;abi_stable/src/std_types/boxed.rs&lt;/code&gt;&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-end/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://perf.wiki.kernel.org/index.php/Tutorial#Counting_with_perf_stat&quot;&gt;Counting with &lt;code&gt;perf stat&lt;/code&gt; — Perf Wiki&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-end/#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn4&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://nnethercote.github.io/perf-book/profiling.html&quot;&gt;Profilers — The Rust Performance Book&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-end/#fnref4&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn5&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://rust-lang.github.io/rfcs/3058-try-trait-v2.html&quot;&gt;Try trait v2 — The Rust RFC Book&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-end/#fnref5&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn6&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#_thread_safety&quot;&gt;Thread safety — Plugins in Rust: Reducing the Pain with Dependencies, NullDeref&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-end/#fnref6&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn7&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://docs.rs/libloading/0.7.3/libloading/struct.Library.html#thread-safety&quot;&gt;Thread-safety — &lt;code&gt;libloading&lt;/code&gt; v0.7.3 docs&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-end/#fnref7&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn8&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/szymonwieloch/rust-dlopen/issues/42&quot;&gt;&lt;code&gt;dlerror&lt;/code&gt; &lt;em&gt;is&lt;/em&gt; thread-safe on some platforms &lt;span class=&quot;gh-number&quot;&gt;szymonwieloch/rust-dlopen#42&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-end/#fnref8&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn9&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#_panicking&quot;&gt;Panicking — Plugins in Rust: Reducing the Pain with Dependencies, NullDeref&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-end/#fnref9&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn10&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#_panicking&quot;&gt;Panicking — Plugins in Rust: Diving into Dynamic Loading, NullDeref&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-end/#fnref10&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn11&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://doc.rust-lang.org/book/ch09-01-unrecoverable-errors-with-panic.html#unwinding-the-stack-or-aborting-in-response-to-a-panic&quot;&gt;Unwinding the Stack or Aborting in Response to a Panic — The Rust Programming Language&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-end/#fnref11&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn12&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-start/#_abi_unstability_its_much_worse_than_it_seems&quot;&gt;ABI unstability, it’s much worse than it seems — Plugins in Rust: Getting Started, NullDeref&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-end/#fnref12&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn13&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/rodrimati1992/abi_stable_crates/tree/69fd90c97268ac0d8d4fe4e83747250e6ed2f3cf#non-features-extremely-unlikely-to-be-added&quot;&gt;Non-features (extremely unlikely to be added) — GitHub rodrimati1992/abi_stable_crates&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-end/#fnref13&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</content>
  </entry>
  <entry>
    <title>Plugins in Rust: Getting our Hands Dirty</title>
    <link href="https://nullderef.com/blog/plugin-impl/" />
    <updated>2022-02-11T00:00:00Z</updated>
    <id>https://nullderef.com/blog/plugin-impl/</id>
    <content type="html">&lt;details class=&quot;toc&quot;&gt;
&lt;summary&gt;Table of Contents&lt;/summary&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#some-prototyping-advice&quot;&gt;Some prototyping advice &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#the-just-make-it-work-approach&quot;&gt;The “just make it work” approach &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#specific-to-rust&quot;&gt;Specific to Rust &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#defining-the-plugin-interface&quot;&gt;Defining the plugin interface &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#recursively-making-everything-repr-c&quot;&gt;Recursively making everything #[repr©] &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#overcoming-problems-with-repr-c&quot;&gt;Overcoming problems with #[repr©] &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#reaching-repr-c-blockers&quot;&gt;Reaching #[repr©] blockers &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#approach-1-avoid-the-type-in-the-first-place&quot;&gt;Approach 1: Avoid the type in the first place &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#approach-2-implement-a-wrapper&quot;&gt;Approach 2: Implement a wrapper &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#approach-3-re-implement-with-repr-c-from-scratch&quot;&gt;Approach 3: Re-implement with #[repr©] from scratch &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#approach-4-simplifying-the-type-at-the-ffi-boundary&quot;&gt;Approach 4: Simplifying the type at the FFI boundary &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#asynchronous-plugins&quot;&gt;Asynchronous plugins &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#async-functions&quot;&gt;async functions &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#channels&quot;&gt;Channels &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#loading-plugins&quot;&gt;Loading plugins &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#separating-runtime-and-interface&quot;&gt;Separating runtime and interface &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#getting-closer-to-deployment&quot;&gt;Getting closer to deployment &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#conclusion&quot;&gt;Conclusion &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/details&gt;
&lt;p&gt;Welcome to one of the last articles of &lt;a href=&quot;https://nullderef.com/series/rust-plugins/&quot;&gt;this series&lt;/a&gt;! &lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable&quot;&gt;Previously&lt;/a&gt;, we covered how to use external dependencies to lessen the work necessary to implement our plugin system. Now that we know how to actually get started, we’ll implement it once and for all.&lt;/p&gt;
&lt;p&gt;I will personally use the crate &lt;a href=&quot;https://crates.io/crates/abi_stable&quot;&gt;&lt;code&gt;abi_stable&lt;/code&gt;&lt;/a&gt;, but the concepts should be roughly the same for any dynamic loading method. Similarly, some of my advice is related to modifying an already existing project of large size. If you’re starting from scratch with a plugin system in mind, it should be the same, just easier in some aspects.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;advice&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;some-prototyping-advice&quot; tabindex=&quot;-1&quot;&gt;Some prototyping advice &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-impl/#some-prototyping-advice&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Before anything else, I’d like to give out some suggestions regarding refactoring with Rust, based on my experience. You’ll be introducing a lot of changes, so it would be wise to do some brief planning beforehand — otherwise it’ll take forever with a complex enough codebase. Maybe this will all be obvious to you, or you may prefer taking a different approach. But it certainly worked for me, so it’s worth a shot. I’m personally very satisfied with how much I’ve learned about the process.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_the_just_make_it_work_approach&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;the-just-make-it-work-approach&quot; tabindex=&quot;-1&quot;&gt;The “just make it work” approach &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-impl/#the-just-make-it-work-approach&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;What I personally struggled the most to get drilled into my head is not getting lost in the details. &lt;strong&gt;First of all, we just want it to work&lt;/strong&gt;. I’ll repeat. First of all, we just want it to &lt;strong&gt;work&lt;/strong&gt;. As long as the plugin system runs, the
following are completely fine:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ugly code (non-idiomatic, repetitive, somewhat messy, etc)&lt;/li&gt;
&lt;li&gt;Non-performant code&lt;/li&gt;
&lt;li&gt;Poor documentation&lt;/li&gt;
&lt;li&gt;No tests&lt;/li&gt;
&lt;li&gt;No clippy&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Or, more specific examples:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;If stuck with lifetimes, just slap a &lt;code&gt;.clone()&lt;/code&gt; in there and call it a day.&lt;/li&gt;
&lt;li&gt;A good ol’ &lt;code&gt;.unwrap()&lt;/code&gt; will make your life easier in case of doubt.&lt;/li&gt;
&lt;li&gt;You can resort to “dirty” or unnecessary type conversions if necessary.&lt;/li&gt;
&lt;li&gt;Just pulling in a new dependency instead of implementing something more tailored to your use-case from scratch will save you a lot of time.&lt;/li&gt;
&lt;li&gt;If something that’s not really important is taking you too much time, you can always resort to a &lt;code&gt;// TODO&lt;/code&gt; or &lt;code&gt;// FIXME&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I will say that not working on tests until the plugin system is running is definitely arguable for those who prefer to follow a test-driven development. However, I personally didn’t feel the need to write any tests in this case; thanks to Rust’s strong type system, it was mostly a compiler-driven development. My progress basically consisted on making some changes and then trying to make the compiler happy about them, repeatedly. I only moved to the testing step once I was happy enough with the results, and everything seemed to work when running it manually.&lt;/p&gt;
&lt;p&gt;Furthermore, premature optimization is the source of all evil. You’re not at a point where that’s important yet. Once you’re done, you can actually spend some time profiling and benchmarking in order to know which optimizations are worth your time. Don’t get me wrong — you can certainly worry about choosing an approach that’s appropriate in terms of performance, which is why I discarded WebAssembly or IPC at the beginning. But definitely give up on trying to avoid a &lt;code&gt;clone&lt;/code&gt; that might not actually affect your program speed after all.&lt;/p&gt;
&lt;p&gt;My point is that every test, clean-up or optimization you try to make at this stage will very likely end up being wasted effort. You’ll reach a blocker at some point later on, and have to rethink and rewrite lots of stuff. When everything compiles, seemingly runs fine, and you consider it a decent approach, you can actually start working on these points. Just keep note of what can be improved so that you don’t forget.&lt;/p&gt;
&lt;p&gt;Note that it is okay to “waste” effort on wrong approaches, because you aren’t really “wasting” it; they’re a necessary step to arrive to the final solution. But it’s painful to delete code you’ve spent time thinking about, so we should at least try to minimize it.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_specific_to_rust&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;specific-to-rust&quot; tabindex=&quot;-1&quot;&gt;Specific to Rust &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-impl/#specific-to-rust&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a name=&quot;_disable_the_annoying_warnings&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;disable-the-annoying-warnings&quot; tabindex=&quot;-1&quot;&gt;Disable the annoying warnings &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-impl/#disable-the-annoying-warnings&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;The first thing I did was remove some warnings the compiler will print when you’re prototyping. By default, a Rust program still compiles with warnings, but in my experience, they’re always in the way when trying to deal with actual errors. For example, I don’t care that an import is unused at all. I’ll fix it once I’m done, but please let me keep trying to “make this work”!&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// TODO: disable and cleanup with `cargo fix` automatically once done&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#![allow(unused)]&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#![allow(dead_code)]&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that I’m not using &lt;code&gt;#![allow(warnings)]&lt;/code&gt;, because most warnings will actually be really helpful when refactoring to avoid early bugs. We just want to get rid of the annoying ones, such as unused functions or imports, or dead code that we haven’t cleaned up yet, which are usually harmless.&lt;/p&gt;
&lt;p&gt;One month later, I was able to finally compile &lt;a href=&quot;https://github.com/tremor-rs/tremor-runtime&quot;&gt;&lt;code&gt;tremor-runtime&lt;/code&gt;&lt;/a&gt; with the initial prototype. This screenshot made my day, as simple as it may seem:&lt;/p&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/blog/plugin-impl/n9qWe-Ga6V-698.avif 698w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/blog/plugin-impl/n9qWe-Ga6V-698.webp 698w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://nullderef.com/blog/plugin-impl/n9qWe-Ga6V-698.png&quot; alt=&quot;Compilation success&quot; width=&quot;698&quot; height=&quot;101&quot;&gt;&lt;/picture&gt;
&lt;p&gt;Afterwards, I removed these &lt;code&gt;allow&lt;/code&gt; statements, ran &lt;code&gt;cargo fix &amp;amp;&amp;amp; cargo fmt&lt;/code&gt;, and cleaned up a bit. &lt;code&gt;cargo fix&lt;/code&gt; is built-in, and will automatically take care of the trivial warnings, such as unused imports, saving &lt;em&gt;lots&lt;/em&gt; of time doing cumbersome work.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_dont_touch_previous_use_statements&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;don-t-touch-previous-use-statements&quot; tabindex=&quot;-1&quot;&gt;Don’t touch previous &lt;code&gt;use&lt;/code&gt; statements &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-impl/#don-t-touch-previous-use-statements&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;As my Pull Request got bigger and bigger, I started running into problems with Git. The parent branch was also undergoing changes from a team of three people, so it was updated quite often. All of this resulted into frequent conflicts, and having to painfully resolve them many times until I got the hang of it.&lt;/p&gt;
&lt;p&gt;Say, you have a number of &lt;code&gt;use&lt;/code&gt; statements at the top of your file. After adding some changes related to the plugin system, you now need to import &lt;code&gt;PdkValue&lt;/code&gt; as well. You’d usually do:&lt;/p&gt;
&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;use tremor_value::{Value, ValueTrait};
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;use tremor_value::{Value, ValueTrait, PdkValue};
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The problem is that if the parent branch later adds or removes one of the items in that &lt;code&gt;use&lt;/code&gt; statement, you may encounter a conflict. Git isn’t smart enough to handle Rust import statements (although that’d be pretty cool). Multiply this by 100 statements, and you might eventually go crazy. You can keep your sanity by adding the new item in a separate block of &lt;code&gt;use&lt;/code&gt;s instead:&lt;/p&gt;
&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token deleted-sign deleted&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;use tremor_value::{Value, ValueTrait};
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;use tremor_value::PdkValue;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Same thing may happen if &lt;code&gt;tremor_value&lt;/code&gt; wasn’t imported and you have to add a new line to the already existing &lt;code&gt;use&lt;/code&gt; block:&lt;/p&gt;
&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;use more_stuff::{X, Y, Z};
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;use tremor_value::PdkValue;
&lt;/span&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;use other_stuff::{A, B, C};
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In my experience, Git also trips up with this sometimes. You can follow the same advice:&lt;/p&gt;
&lt;pre class=&quot;language-diff&quot;&gt;&lt;code class=&quot;language-diff&quot;&gt;&lt;span class=&quot;token unchanged&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;use more_stuff::{X, Y, Z};
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;use other_stuff::{A, B, C};
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;use tremor_value::PdkValue;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In summary, you can just create a new block after all the previous &lt;code&gt;use&lt;/code&gt;s and add your stuff in there. You can clean it up at the end or in a different pull request.&lt;/p&gt;
&lt;p&gt;This is harder to do if you’re &lt;em&gt;removing&lt;/em&gt; &lt;code&gt;use&lt;/code&gt; statements. But if you also followed the previous section about ignoring annoying warnings, it won’t be a problem in the first place. You can just leave the unused imports and clean up when you’re done with the PR.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_defining_the_plugin_interface&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;defining-the-plugin-interface&quot; tabindex=&quot;-1&quot;&gt;Defining the plugin interface &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-impl/#defining-the-plugin-interface&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The first step that we can do is define the interface of the plugin system, i.e., what a plugin binary must implement in order to be loadable by the runtime. If you’re doing this over an already existing codebase, you’ll probably get tons of errors. We’ll ignore them for now; this is only our first sketch, and you’ll end up changing it a thousand times anyway. Some types in the interface may not exist yet, or they may not be meant to be used for FFI. But it’ll serve us as an initial list of things to work on.&lt;/p&gt;
&lt;p&gt;In my case, it first looked as follows. The specifics about how this works with &lt;a href=&quot;https://crates.io/crates/abi_stable&quot;&gt;&lt;code&gt;abi_stable&lt;/code&gt;&lt;/a&gt; are explained in the &lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/&quot;&gt;previous post&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/// This type represents a connector plugin that has been loaded with&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/// `abi_stable`. It serves as a builder, making it possible to construct a&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/// trait object of `RawConnector`.&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#[repr(C)]&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#[derive(StableAbi)]&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#[sabi(kind(Prefix))]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;ConnectorMod&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// the type of the connector&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; connector_type&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConnectorType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;/// create a connector from the given `id` and `config`&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;///&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// # Errors&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;///  * If the config is invalid for the connector&lt;/span&gt;
    &lt;span class=&quot;token attribute attr-name&quot;&gt;#[sabi(last_prefix_field)]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; from_config&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        config&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ROption&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FfiFuture&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RResult&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;BoxedRawConnector&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/// Marking `ConnectorMod` as the main module in this plugin. Note that&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/// `ConnectorMod_Ref` is just a pointer to the prefix of `ConnectorMod`.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RootModule&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConnectorMod_Ref&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// The name of the dynamic library&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;BASE_NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;connector&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// The name of the library for logging and similars&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;connector&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// The version of this plugin&#39;s crate&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;VERSION_STRINGS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;VersionStrings&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token macro property&quot;&gt;package_version_strings!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;/// Implements the `RootModule::root_module_statics` function, which is the&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// only required method for the `RootModule` trait.&lt;/span&gt;
    &lt;span class=&quot;token macro property&quot;&gt;declare_root_module_statics!&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ConnectorMod_Ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My task was to turn the &lt;code&gt;Connector&lt;/code&gt; trait into a plugin. All the &lt;code&gt;Connector&lt;/code&gt; implementors were meant to be constructed with &lt;code&gt;ConnectorBuilder&lt;/code&gt;, and from that moment on Tremor was able to use them generically with &lt;code&gt;dyn Connector&lt;/code&gt;. Thus, I thought the best idea would be to turn &lt;code&gt;ConnectorBuilder&lt;/code&gt; into my &lt;code&gt;RootModule&lt;/code&gt; under the name &lt;code&gt;ConnectorMod&lt;/code&gt;. Once the plugin was loaded, it would be possible to construct the connector with the &lt;code&gt;from_config&lt;/code&gt; function. I used &lt;code&gt;abi_stable::sabi_trait&lt;/code&gt;’s macro functionality, making &lt;code&gt;dyn&lt;/code&gt; usage possible within FFI.&lt;/p&gt;
&lt;p&gt;For reference, when I first wrote &lt;code&gt;ConnectorMod&lt;/code&gt;, &lt;code&gt;Value&lt;/code&gt; wasn’t even &lt;code&gt;#[repr(C)]&lt;/code&gt;. I had also added the &lt;code&gt;#[sabi_trait]&lt;/code&gt; attribute to the &lt;code&gt;RawConnector&lt;/code&gt; trait declaration, but the types used there weren’t &lt;code&gt;#[repr(C)]&lt;/code&gt; either. So I had tons of errors everywhere, but that was OK. I would be working on them step by step until it compiled again.&lt;/p&gt;
&lt;p&gt;If you’re using &lt;a href=&quot;https://crates.io/crates/libloading&quot;&gt;&lt;code&gt;libloading&lt;/code&gt;&lt;/a&gt; directly then you would be implementing
the interface via a struct with function pointers instead, and you’d need to
store metadata about the plugin with constants. But in the end, it boils down to
the same thing; just with different amounts of boilerplate.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_recursively_making_everything_reprc&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;recursively-making-everything-repr-c&quot; tabindex=&quot;-1&quot;&gt;Recursively making everything &lt;code&gt;#[repr(C)]&lt;/code&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-impl/#recursively-making-everything-repr-c&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now, this is the actually complicated part. The previous step may have seemed simple, but you might find yourself falling into madness as you realize that you need to make all the types in your interface &lt;code&gt;#[repr(C)]&lt;/code&gt;, and also all the fields each of these types hold, and so on…&lt;/p&gt;
&lt;p&gt;It’s very likely that you’ll eventually find types without an FFI alternative in &lt;a href=&quot;https://crates.io/crates/abi_stable&quot;&gt;&lt;code&gt;abi_stable&lt;/code&gt;&lt;/a&gt;. These will most likely be external types, but things like async are a bit complicated to deal with as well. In the case of Tremor, the most problematic part was the &lt;code&gt;Value&lt;/code&gt; type. It’s used to represent a JSON-like payload; roughly defined as follows:&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;value_decl&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// Static values (integers, booleans, etc)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Static&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;StaticNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// String type&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// Array type&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// Object type&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Box&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// A binary type&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Bytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In order to be able to use &lt;code&gt;Value&lt;/code&gt; in the plugin system, it can be converted to:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[repr(C)]&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#[derive(StableAbi)]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Only necessary for abi_stable&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// Static values (integers, booleans, etc)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Static&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;StaticNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// String type&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// Array type&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RVec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// Object type&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RBox&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RHashMap&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// A binary type&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Bytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RVec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first problem arises in the &lt;code&gt;Static&lt;/code&gt; variant: &lt;a href=&quot;https://docs.rs/value-trait/latest/value_trait/enum.StaticNode.html&quot;&gt;&lt;code&gt;StaticNode&lt;/code&gt;&lt;/a&gt; is a &lt;code&gt;#[repr(Rust)]&lt;/code&gt; &lt;em&gt;external&lt;/em&gt; type. It’s from our &lt;a href=&quot;https://crates.io/crates/value_trait&quot;&gt;&lt;code&gt;value_trait&lt;/code&gt;&lt;/a&gt; dependency, and it may hold different basic types: numbers, booleans, or just nothing.&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;StaticNode&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;I64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;i64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;U64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;F64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;f64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This could be fixed by simply applying the very same procedure again (hence &lt;em&gt;recursively&lt;/em&gt; until everything is &lt;code&gt;#[repr(C)]&lt;/code&gt;). Here it will finally work because there aren’t any other &lt;code&gt;#[repr(Rust)]&lt;/code&gt; types in &lt;code&gt;StaticNode&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[cfg_attr(feature = &lt;span class=&quot;token string&quot;&gt;&quot;abi_stable&quot;&lt;/span&gt;, repr(C))]&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#[cfg_attr(feature = &lt;span class=&quot;token string&quot;&gt;&quot;abi_stable&quot;&lt;/span&gt;, derive(abi_stable::StableAbi))]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;StaticNode&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;I64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;i64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;U64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token constant&quot;&gt;F64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;f64&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Bool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Since it’s an external library, we’ll have to make a Pull Request and hope that the author is okay with the changes. &lt;code&gt;abi_stable&lt;/code&gt; should be optional so that this change is applied only to those that actually need &lt;code&gt;#[repr(C)]&lt;/code&gt; in the library. You could also go a step further and differentiate between enabling &lt;code&gt;#[repr(C)]&lt;/code&gt; and deriving &lt;code&gt;StableAbi&lt;/code&gt; for those that don’t need the latter.&lt;/p&gt;
&lt;p style=&quot;text-align:center&quot;&gt;
  &lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&amp;nbsp;&lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/simd-lite/value-trait/pull/14&quot;&gt;Add support for StableAbi &lt;span class=&quot;gh-number&quot;&gt;simd-lite/value-trait#14&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_overcoming_problems_with_reprc&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;overcoming-problems-with-repr-c&quot; tabindex=&quot;-1&quot;&gt;Overcoming problems with &lt;code&gt;#[repr(C)]&lt;/code&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-impl/#overcoming-problems-with-repr-c&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Awesome. We got &lt;code&gt;Value&lt;/code&gt; working now for FFI. Right? No? Oh. It seems like the compiler strongly disagrees. By changing the variants of &lt;code&gt;Value&lt;/code&gt;, a great amount of the code that used it will now fail to compile in multiple ways:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Won&#39;t work because Value::Array holds an RVec now&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;That’s the easiest one: we just need to change &lt;code&gt;Vec&lt;/code&gt; to &lt;code&gt;RVec&lt;/code&gt; and it should be fine. The types in &lt;a href=&quot;https://crates.io/crates/abi_stable&quot;&gt;&lt;code&gt;abi_stable&lt;/code&gt;&lt;/a&gt; are meant to be a drop-in replacement for the ones in &lt;code&gt;std&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RVec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It gets a bit more complicated when the old types are exposed in methods, because you have to decide whether to expand the FFI boundary from the &lt;em&gt;internals&lt;/em&gt; of &lt;code&gt;Value&lt;/code&gt; to the &lt;em&gt;users&lt;/em&gt; of &lt;code&gt;Value&lt;/code&gt;. For instance, the &lt;code&gt;Value::Object&lt;/code&gt; variant holds a &lt;code&gt;RHashMap&lt;/code&gt; now, but the method &lt;code&gt;Value::as_object&lt;/code&gt; used to return a reference to a &lt;code&gt;HashMap&lt;/code&gt;. You’ll get another error in there, which raises a decision that must be made: returning &lt;code&gt;RHashMap&lt;/code&gt; or adding an internal conversion to &lt;code&gt;HashMap&lt;/code&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Original code&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;as_object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// Problem: `m` is a `RHashMap` now, but the function returns a&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// `HashMap`.&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;//&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// Solution 1: change the return type to `RHashMap`&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// Solution 2: convert `m` to a `HashMap` with `m.into()`&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            _ &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;If the return type is changed to &lt;code&gt;RHashMap&lt;/code&gt;, almost every caller to &lt;code&gt;as_object&lt;/code&gt; in the program will now fail to compile because they expect a &lt;code&gt;HashMap&lt;/code&gt;. You’ll have to clean it up one by one and figure out how &lt;code&gt;RHashMap&lt;/code&gt; can be used in that case instead.&lt;/p&gt;
&lt;p&gt;This can be messy because in order to avoid conversions, your plugin system will &lt;em&gt;infect&lt;/em&gt; the entire codebase. You may quickly find yourself propagating the usage of &lt;code&gt;RHashMap&lt;/code&gt; everywhere, even when the system isn’t that important. For example, &lt;code&gt;Value&lt;/code&gt; also appeared in &lt;a href=&quot;https://www.tremor.rs/docs/tremor-script/index&quot;&gt;Trickle&lt;/a&gt;’s implementation, the scripting language used to configure Tremor’s pipelines. Having to use &lt;code&gt;RHashMap&lt;/code&gt; in there was a bit confusing, and I was modifying lots of files unrelated to the plugin system.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;If you perform an internal conversion to &lt;code&gt;HashMap&lt;/code&gt; in &lt;code&gt;as_object&lt;/code&gt; we’ll avoid all of these errors, at the cost of adding a small overhead. It’s by far the easiest choice, but if &lt;code&gt;Value::as_object&lt;/code&gt; is frequently used in, e.g., your hot loop, you may notice a considerable performance degradation.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;I &lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#_type_conversions&quot;&gt;already investigated in the past&lt;/a&gt;, and the good news is that converting between types in &lt;code&gt;std&lt;/code&gt; and &lt;code&gt;abi_stable&lt;/code&gt; is $O(1)$. Most of the times it’s equivalent to a pointer cast or a match. So here’s when the “just make it work” approach is useful: we’ll just keep the FFI boundary minimal and add conversions as early as possible. After we’re done, we’ll see if there are any performance issues, and then work on them.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_reaching_reprc_blockers&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;reaching-repr-c-blockers&quot; tabindex=&quot;-1&quot;&gt;Reaching &lt;code&gt;#[repr(C)]&lt;/code&gt; blockers &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-impl/#reaching-repr-c-blockers&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;That was my first attempt at making &lt;code&gt;Value&lt;/code&gt; FFI-compatible, and unfortunately, it didn’t end there. Converting from &lt;code&gt;std&lt;/code&gt; to &lt;a href=&quot;https://crates.io/crates/abi_stable&quot;&gt;&lt;code&gt;abi_stable&lt;/code&gt;&lt;/a&gt; is a relatively painless experience; their usage intends to be the same. The only issue I found in that regard is that some methods from &lt;code&gt;std&lt;/code&gt; weren’t available in &lt;a href=&quot;https://crates.io/crates/abi_stable&quot;&gt;&lt;code&gt;abi_stable&lt;/code&gt;&lt;/a&gt; yet because it’s not updated as regularly. Usually, you can just copy-paste the implementation from &lt;code&gt;std&lt;/code&gt; into &lt;a href=&quot;https://crates.io/crates/abi_stable&quot;&gt;&lt;code&gt;abi_stable&lt;/code&gt;&lt;/a&gt;&#39;s and create a new Pull Request, which is what I did a few times:&lt;/p&gt;
&lt;p&gt;
  &lt;/p&gt;&lt;div style=&quot;text-align:center&quot;&gt;
    &lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&amp;nbsp;&lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rodrimati1992/abi_stable_crates/pull/58&quot;&gt;Add support for .keys() and .values() in RHashMap &lt;span class=&quot;gh-number&quot;&gt;rodrimati1992/abi_stable_crates#58&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;
  &lt;/div&gt;
  &lt;div style=&quot;text-align:center&quot;&gt;
    &lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&amp;nbsp;&lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rodrimati1992/abi_stable_crates/pull/59&quot;&gt;Implement &lt;code&gt;Index&lt;/code&gt; for slices and vectors &lt;span class=&quot;gh-number&quot;&gt;rodrimati1992/abi_stable_crates#59&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;
  &lt;/div&gt;
  &lt;div style=&quot;text-align:center&quot;&gt;
    &lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&amp;nbsp;&lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rodrimati1992/abi_stable_crates/pull/61&quot;&gt;Support for &lt;code&gt;f32&lt;/code&gt; and &lt;code&gt;f64&lt;/code&gt; &lt;span class=&quot;gh-number&quot;&gt;rodrimati1992/abi_stable_crates#61&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;
  &lt;/div&gt;
  &lt;div style=&quot;text-align:center&quot;&gt;
    &lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&amp;nbsp;&lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rodrimati1992/abi_stable_crates/pull/68&quot;&gt;Implement &lt;code&gt;ROption::as_deref&lt;/code&gt; &lt;span class=&quot;gh-number&quot;&gt;rodrimati1992/abi_stable_crates#68&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;
  &lt;/div&gt;
  &lt;div style=&quot;text-align:center&quot;&gt;
    &lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&amp;nbsp;&lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rodrimati1992/abi_stable_crates/pull/70&quot;&gt;Implement RVec::append &lt;span class=&quot;gh-number&quot;&gt;rodrimati1992/abi_stable_crates#70&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;
  &lt;/div&gt;
  &lt;div style=&quot;text-align:center&quot;&gt;
    &lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&amp;nbsp;&lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rodrimati1992/abi_stable_crates/pull/82&quot;&gt;Implement &lt;code&gt;ROption::{ok_or,ok_or_else}&lt;/code&gt; &lt;span class=&quot;gh-number&quot;&gt;rodrimati1992/abi_stable_crates#82&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;
  &lt;/div&gt;
&lt;p&gt;&lt;/p&gt;
&lt;p&gt;However, this stops being as “easy” when you have to convert from &lt;em&gt;an external library&lt;/em&gt; to &lt;a href=&quot;https://crates.io/crates/abi_stable&quot;&gt;&lt;code&gt;abi_stable&lt;/code&gt;&lt;/a&gt;. I lied at the beginning of the article: the &lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#value_decl&quot;&gt;declaration of &lt;code&gt;Value&lt;/code&gt;&lt;/a&gt; was an oversimplification. For performance reasons, Tremor actually uses &lt;a href=&quot;https://crates.io/crates/halfbrown&quot;&gt;&lt;code&gt;halfbrown&lt;/code&gt;&lt;/a&gt;&#39;s implementation of a hash map instead of &lt;code&gt;std::collections::HashMap&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: &lt;a href=&quot;https://crates.io/crates/halfbrown&quot;&gt;&lt;code&gt;halfbrown&lt;/code&gt;&lt;/a&gt; is based on &lt;a href=&quot;https://crates.io/crates/hashbrown&quot;&gt;&lt;code&gt;hashbrown&lt;/code&gt;&lt;/a&gt;, which was, in fact, merged into the standard library at some point&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;. Although with this plugin system we’re suffering the consequences of not having a stable ABI, seeing that it enables things like that makes me less bitter.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://crates.io/crates/halfbrown&quot;&gt;&lt;code&gt;halfbrown&lt;/code&gt;&lt;/a&gt; has some additional functionality over &lt;code&gt;std&lt;/code&gt;’s implementation. Some of it is actually available on Nightly, but for that reason it’s not meant to be in the stable &lt;code&gt;RHashMap&lt;/code&gt; either. This extra functionality is used in Tremor for example with &lt;a href=&quot;https://doc.rust-lang.org/std/collections/hash_map/struct.HashMap.html#method.raw_entry&quot;&gt;&lt;code&gt;raw_entry&lt;/code&gt;&lt;/a&gt;. There is an optimization for JSON handling that consists on memoizing the hash of a known item in a map in order to access its value directly with it&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;. After switching to &lt;code&gt;RHashMap&lt;/code&gt;, this becomes an impossible task.&lt;/p&gt;
&lt;p&gt;Even if I managed to fix the hash map mess, the same story repeats itself for &lt;code&gt;Cow&lt;/code&gt;. Tremor uses &lt;a href=&quot;https://crates.io/crates/beef&quot;&gt;&lt;code&gt;beef&lt;/code&gt;&lt;/a&gt;&#39;s &lt;a href=&quot;https://docs.rs/beef/latest/beef/generic/struct.Cow.html&quot;&gt;&lt;code&gt;Cow&lt;/code&gt;&lt;/a&gt; instead of &lt;code&gt;std::borrow::Cow&lt;/code&gt; because it’s faster and more compact, at the cost of a slightly different usage.&lt;/p&gt;
&lt;p&gt;There are a few possible ways to approach these kinds of issues:&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;avoid&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;approach-1-avoid-the-type-in-the-first-place&quot; tabindex=&quot;-1&quot;&gt;Approach 1: Avoid the type in the first place &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-impl/#approach-1-avoid-the-type-in-the-first-place&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As always, we’ll try to follow the “just make it work” advice in here. It’s a perfectly valid solution to just comment out the optimizations and add a &lt;code&gt;// TODO&lt;/code&gt; above so that they can be reviewed later. You might be asking for too much complexity in your plugin system; limiting yourself to the functionality in &lt;code&gt;std&lt;/code&gt; may be more than enough for now. You’ll now see that it’s always possible to fix this properly. It just might be too much effort at the moment.&lt;/p&gt;
&lt;p&gt;In &lt;code&gt;Value&lt;/code&gt;’s specific case, it seems like removing the optimizations that are giving us trouble is the easiest way to fix this problem. And it would, if removing code wasn’t also tricky when the optimizations aren’t performed transparently to the hash map functionality. After trying to remove them I eventually gave up because I considered it was getting out of hands with so many changes.&lt;/p&gt;
&lt;p&gt;These are &lt;strong&gt;120&lt;/strong&gt; errors after attempting to remove the optimizations, most of them about lifetime hell (more about this at the end of the article):&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/blog/plugin-impl/4LtaaFmzzx-1070.avif 1070w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/blog/plugin-impl/4LtaaFmzzx-1070.webp 1070w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://nullderef.com/blog/plugin-impl/4LtaaFmzzx-1070.png&quot; alt=&quot;Compilation errors&quot; width=&quot;1070&quot; height=&quot;154&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_implement_a_wrapper&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;approach-2-implement-a-wrapper&quot; tabindex=&quot;-1&quot;&gt;Approach 2: Implement a wrapper &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-impl/#approach-2-implement-a-wrapper&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Another possibility is to write a &lt;em&gt;wrapper&lt;/em&gt; for &lt;a href=&quot;https://crates.io/crates/halfbrown&quot;&gt;&lt;code&gt;halfbrown&lt;/code&gt;&lt;/a&gt;. Opaque types, for instance, may be used to wrap the functionality of an underlying type that’s not FFI-safe, as I covered in previous articles. This is what &lt;a href=&quot;https://crates.io/crates/abi_stable&quot;&gt;&lt;code&gt;abi_stable&lt;/code&gt;&lt;/a&gt; does in its &lt;code&gt;external_types&lt;/code&gt; module for crates like &lt;a href=&quot;https://crates.io/crates/crossbeam&quot;&gt;&lt;code&gt;crossbeam&lt;/code&gt;&lt;/a&gt; or &lt;a href=&quot;https://crates.io/crates/parking_lot&quot;&gt;&lt;code&gt;parking_lot&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;However, as you may see with the &lt;a href=&quot;https://github.com/rodrimati1992/abi_stable_crates/tree/edfb2a97a7b5d7ecbc29c1f9f115f61e26f42da6/abi_stable/src/external_types&quot;&gt;already existing examples&lt;/a&gt;, implementing wrappers can be quite a cumbersome task. And even after you’re done you’ll have to keep them up to date, so this will increase your maintainance burden. &lt;a href=&quot;https://crates.io/crates/halfbrown&quot;&gt;&lt;code&gt;halfbrown&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://crates.io/crates/beef&quot;&gt;&lt;code&gt;beef&lt;/code&gt;&lt;/a&gt; are somewhat complex libraries, so I decided this wasn’t the best choice at that moment for &lt;code&gt;Value&lt;/code&gt;. I did use this approach a lot in other cases, so I’ve included an example in a &lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#opaque&quot;&gt;later section&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_re_implement_with_reprc_from_scratch&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;approach-3-re-implement-with-repr-c-from-scratch&quot; tabindex=&quot;-1&quot;&gt;Approach 3: Re-implement with &lt;code&gt;#[repr(C)]&lt;/code&gt; from scratch &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-impl/#approach-3-re-implement-with-repr-c-from-scratch&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Similar to implementing a wrapper, but on steroids. It might seem like overkill, but as far as I know it’s the only choice in some scenarios, because we can make sure the type is as performant as it can get. In &lt;code&gt;Value&lt;/code&gt;’s example, the problematic types are part of optimizations, so writing a wrapper for them may have a performance hit and render them useless (e.g., if we used opaque types we would introduce at least a mandatory pointer indirection).&lt;/p&gt;
&lt;p&gt;If this part of the project is important enough, or you consider that there’s enough manpower, then it might not be such a bad idea to create a new implementation with your use-case in mind. It’s actually why Tremor’s &lt;code&gt;Value&lt;/code&gt; was created in the first place; &lt;code&gt;simd_json::Value&lt;/code&gt; wasn’t flexible enough for them, so they decided to define their own version. Same thing could be applied for your plugin system.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_simplifying_the_type_at_the_ffi_boundary&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;approach-4-simplifying-the-type-at-the-ffi-boundary&quot; tabindex=&quot;-1&quot;&gt;Approach 4: Simplifying the type at the FFI boundary &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-impl/#approach-4-simplifying-the-type-at-the-ffi-boundary&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The last idea I came up with was the easiest one: creating a copy of &lt;code&gt;Value&lt;/code&gt; meant to be used &lt;em&gt;only&lt;/em&gt; for FFI communication, &lt;code&gt;PdkValue&lt;/code&gt;:&lt;/p&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/blog/plugin-impl/AW-qEhPljZ-825.avif 825w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/blog/plugin-impl/AW-qEhPljZ-825.webp 825w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://nullderef.com/blog/plugin-impl/AW-qEhPljZ-825.png&quot; alt=&quot;runtime/plugin diagram&quot; width=&quot;825&quot; height=&quot;479&quot;&gt;&lt;/picture&gt;
&lt;p&gt;Since it’s a new type, we won’t run into the breaking changes I showed in the &lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#avoid&quot;&gt;Avoid the type in the first place&lt;/a&gt; section, and it’s quite easy to implement:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[repr(C)]&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#[derive(StableAbi)]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;PdkValue&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// Static values (integers, booleans, etc)&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Static&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;StaticNode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// String type&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// Array type&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RVec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PdkValue&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// Object type&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RBox&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RHashMap&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PdkValue&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// A binary type&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Bytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RVec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We don’t need to write any additional methods for the new &lt;code&gt;PdkValue&lt;/code&gt;, only its conversions to and from the regular &lt;code&gt;Value&lt;/code&gt;. This would be equivalent to, instead of passing a &lt;code&gt;Vec&lt;/code&gt; to the interface, replacing it with a &lt;code&gt;*const u8&lt;/code&gt; for the data and a &lt;code&gt;u32&lt;/code&gt; for the length. We’re just simplifying the types at the FFI boundary, and then maybe converting them back for access to the full functionality.&lt;/p&gt;
&lt;p&gt;The problem in my case is that these conversions are now $O(n)$ instead of $O(1)$, because I have to iterate the objects and arrays in order to convert its inner values as well:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;From&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PdkValue&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;original&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;Self&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;match&lt;/span&gt; original &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// No conversion needed; `StaticNode` implements `StableAbi`&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Static&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PdkValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Static&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// This conversion is cheap&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PdkValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;into&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// This unfortunately requires iterating the array&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;into_iter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Into&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;into&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;PdkValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// This unfortunately requires iterating the map and a new&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// allocation&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; m &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; m&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;into_iter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Into&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;into&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;PdkValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;RBox&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// This conversion is cheap&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Bytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PdkValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Bytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;conv_u8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Same as before, but inversely&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;From&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PdkValue&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;original&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PdkValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;Self&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;match&lt;/span&gt; original &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;PdkValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Static&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Static&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;PdkValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;into&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;PdkValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; a &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;into_iter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Into&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;into&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Array&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;PdkValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token comment&quot;&gt;// No Box dereference move magic with RBox, we call `into_inner`&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; m &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RBox&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;into_inner&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; m &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; m
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;into_iter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token closure-params&quot;&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Tuple2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;k&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;into&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;into&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Box&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;m&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;PdkValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Bytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Bytes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;conv_u8_inv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then we can use it like:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// This is implemented in the plugin. It will work because `PdkValue` is&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// FFI-safe.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;plugin_stuff&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PdkValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;do_stuff&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// This is implemented in the runtime&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;runtime_wrapper&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;plugin_stuff&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;into&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Surprisingly easy to get working! Two problems, though:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Usability&lt;/strong&gt;: the snippet of code above shows that the conversions introduce some noise in our code, as opposed to having a single &lt;code&gt;#[repr(C)]&lt;/code&gt; &lt;code&gt;Value&lt;/code&gt;. It could be worse, but it can get annoying when you have many &lt;code&gt;Value&lt;/code&gt;s in your runtime and have to pass them to the plugins.&lt;/p&gt;
&lt;p&gt;This is actually trivial enough to simplify with a macro — perferrably procedural — at the cost of introducing more complexity. We’re currently following “just make it work” so we’ll leave that for the future.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Performance&lt;/strong&gt;: passing the value to the plugin and converting it back in there means we’re iterating the data twice. With experiments that I’ll include in the next article, I found out that these conversions make up 5 to 10% of Tremor’s execution time. Which is less than I expected, but still not good enough for production.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;For the first version of the plugin system, this is the solution that I ended up using for &lt;code&gt;Value&lt;/code&gt;. Most of the time I spent was just trying the different available approaches, and this one won in simplicity by a huge margin. After being done, I would be able to decide if the performance hit was bad enough, and then maybe switch to a different one. Creating &lt;code&gt;PdkValue&lt;/code&gt; wasn’t a big time investment, so it wouldn’t really feel like a waste.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_asynchronous_plugins&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;asynchronous-plugins&quot; tabindex=&quot;-1&quot;&gt;Asynchronous plugins &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-impl/#asynchronous-plugins&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a name=&quot;_async_functions&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;async-functions&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;async&lt;/code&gt; functions &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-impl/#async-functions&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I’ve always wanted to avoid communication primitives between plugins and runtime other than plain synchronous calls. But this might be inevitable if your program uses asynchronous programming heavily, which is the case of Tremor. Anyhow, turns out that using &lt;code&gt;async&lt;/code&gt; in FFI isn’t that complicated!&lt;/p&gt;
&lt;p&gt;In the previous post I introduced the &lt;a href=&quot;https://crates.io/crates/async_ffi&quot;&gt;&lt;code&gt;async_ffi&lt;/code&gt;&lt;/a&gt; crate, which exports FFI-compatible &lt;code&gt;Future&lt;/code&gt;s. It’s quite easy to use; here’s an example from the docs:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;async_ffi&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;FfiFuture&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FutureExt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token attribute attr-name&quot;&gt;#[no_mangle]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;work&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arg&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FfiFuture&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;move&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; ret &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;do_some_io&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;arg&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;do_some_sleep&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        ret
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;into_ffi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The types in &lt;a href=&quot;https://crates.io/crates/async_ffi&quot;&gt;&lt;code&gt;async_ffi&lt;/code&gt;&lt;/a&gt; implement &lt;code&gt;Future&lt;/code&gt;, so invoking that function is as easy as usual: just adding &lt;code&gt;.await&lt;/code&gt; after the function call.&lt;/p&gt;
&lt;p&gt;It’s admittedly a bit ugly to use &lt;code&gt;async move { }.into_ffi()&lt;/code&gt; everywhere, specially because it increases the indentation in one level. But that’s something that can be fixed with a procedural macro in the future:&lt;/p&gt;
&lt;p style=&quot;text-align:center&quot;&gt;
  &lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&amp;nbsp;&lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/oxalica/async-ffi/issues/12&quot;&gt;Procedural macro for boilerplate &lt;span class=&quot;gh-number&quot;&gt;oxalica/async-ffi#12&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;The only problem I found was that the futures didn’t implement &lt;code&gt;StableAbi&lt;/code&gt;, so it wasn’t possible to use them with &lt;a href=&quot;https://crates.io/crates/abi_stable&quot;&gt;&lt;code&gt;abi_stable&lt;/code&gt;&lt;/a&gt;. It took me a while to understand the crate, but it’s nothing a Pull Request can’t fix:&lt;/p&gt;
&lt;p style=&quot;text-align:center&quot;&gt;
  &lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&amp;nbsp;&lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/oxalica/async-ffi/pull/10&quot;&gt;Support for &lt;code&gt;abi_stable&lt;/code&gt; &lt;span class=&quot;gh-number&quot;&gt;oxalica/async-ffi#10&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;One concern here may be performance. I imagine that it’s not a huge problem because the crate is actualy quite small and only introduces some pointer juggling. I will confirm this in the next post with some benchmarks, though.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_channels&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;channels&quot; tabindex=&quot;-1&quot;&gt;Channels &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-impl/#channels&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Tremor also needs channels for asynchronous communication. For example, a connector may need to indicate the runtime that the connection has been lost at any point of its execution. We can’t delay that until the next synchronous call from the runtime because we don’t know when that might happen, and we want to keep Tremor low-latency.&lt;/p&gt;
&lt;p&gt;We have three options here:&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_use_abi_stables_alternatives&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;option-1-use-abi-stable-s-alternatives&quot; tabindex=&quot;-1&quot;&gt;Option 1: Use &lt;code&gt;abi_stable&lt;/code&gt;’s alternatives &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-impl/#option-1-use-abi-stable-s-alternatives&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Turns out &lt;code&gt;abi_stable&lt;/code&gt; includes an FFI-safe wrapper for &lt;a href=&quot;https://crates.io/crates/crossbeam&quot;&gt;&lt;code&gt;crossbeam&lt;/code&gt;&lt;/a&gt;. We could just switch the usage of &lt;code&gt;Sender&amp;lt;T&amp;gt;&lt;/code&gt; to &lt;a href=&quot;https://docs.rs/abi_stable/latest/abi_stable/external_types/crossbeam_channel/struct.RSender.html&quot;&gt;&lt;code&gt;RSender&amp;lt;T&amp;gt;&lt;/code&gt;&lt;/a&gt; and that’s it.&lt;/p&gt;
&lt;p&gt;Problem: Tremor actually uses asynchronous channels, such as &lt;a href=&quot;https://docs.rs/async-std/latest/async_std/channel/index.html&quot;&gt;&lt;code&gt;async_std::channel&lt;/code&gt;&lt;/a&gt;, so it wasn’t as easy as changing to &lt;code&gt;crossbeam&lt;/code&gt;. We want to be able to poll for events without blocking the thread.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_callbacks&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;option-2-callbacks&quot; tabindex=&quot;-1&quot;&gt;Option 2: Callbacks &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-impl/#option-2-callbacks&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;If your use-case is simple enough, callbacks might be sufficient. I &lt;a href=&quot;https://github.com/marioortizmanero/pdk-experiments/tree/master/callbacks&quot;&gt;experimented a bit&lt;/a&gt; with them and it’s a good alternative if you:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Don’t need to listen for events actively (calling &lt;code&gt;rx.recv()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Don’t need to access much data other than the callback’s parameters. Note that it’s a function and not a closure, so you’d need globals, and that can get nasty real quick.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The type definition in the shared crate:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[repr(C)]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;ConnectorContext&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    id&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    callback&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The callback definition and the plugin loading in the runtime:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// This will run a few plugins concurrently.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// You still have access to some resources in the runtime, but don&#39;t&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// abuse it!&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;COUNT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AtomicI32&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AtomicI32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;callback&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;raw callback invoked! {x:?}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token constant&quot;&gt;COUNT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;fetch_add&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Ordering&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Relaxed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; handles &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; id &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NUM_THREADS&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        handles&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;push&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;thread&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;spawn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;move&lt;/span&gt; &lt;span class=&quot;token closure-params&quot;&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; ctx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConnectorContext&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; id&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; callback &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
            &lt;span class=&quot;token function&quot;&gt;plugin_fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; handle &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; handles &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        handle&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;join&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Final count: {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;COUNT&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;load&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Ordering&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Relaxed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The functionality implementation in the plugin:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// This only invokes the callback with its own ID.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;plugin_fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ConnectorContext&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Asynchronous communication!&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;thread&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;spawn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;move&lt;/span&gt; &lt;span class=&quot;token closure-params&quot;&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;callback&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;ctx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;id&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can even wrap the function pointer up so that the usage can remain as &lt;code&gt;sender.send(value)&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Sender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    callback&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Sender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;callback&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;Self&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;Self&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; callback &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; t&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;callback&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;t&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, the use-cases for this are very limited. If you don’t really need to access anything in the runtime you might as well just have a regular function in the shared crate. And if you need more complex functionality you might be better off with a regular channel.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;opaque&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;option-3-opaque-types&quot; tabindex=&quot;-1&quot;&gt;Option 3: Opaque types &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-impl/#option-3-opaque-types&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;We can resort to opaque types for anything we can’t remove or simplify. This is what I did in order to have asynchronous channels available on the plugins.&lt;/p&gt;
&lt;p&gt;The interface in the shared crate, using &lt;code&gt;abi_stable&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[abi_stable::sabi_trait]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;trait&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;SenderOpaque&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Send&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// Send a message to the runtime&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reply&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Reply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BorrowingFfiFuture&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RResult&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/// Alias for the FFI-safe sender, boxed&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;BoxedSender&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SenderOpaque_TO&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;static&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RBox&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The plugin loading in the runtime:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SenderOpaque&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;async_std&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;channel&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Sender&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Reply&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reply&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Reply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BorrowingFfiFuture&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RResult&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;move&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;reply&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map_err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token closure-params&quot;&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;e&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RError&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;into&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;into_ffi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;runtime&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Non FFI-safe type&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; rx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;async_std&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;channel&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unbounded&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Maybe multiple times in different asynchronous tasks&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; reply_tx &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BoxedSender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from_value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;tx&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TD_Opaque&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    library&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;plugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;reply_tx&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Waiting for events&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;while&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;reply&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; rx&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;recv&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Got a reply from a plugin: {reply:?}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The functionality implementation in the plugin:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;plugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;sender&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;BoxedSender&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Asynchronous communication!&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;task&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;spawn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;move&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        sender&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Reply&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Fail&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a name=&quot;_loading_plugins&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;loading-plugins&quot; tabindex=&quot;-1&quot;&gt;Loading plugins &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-impl/#loading-plugins&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Loading plugins in the runtime was actually the easiest part. &lt;code&gt;abi_stable&lt;/code&gt; takes care of most of the work; the only thing that was missing for me is a way to &lt;em&gt;find&lt;/em&gt; the plugins. This greatly depends on your program: you may hardcode the location, have them passed as CLI arguments, etc.&lt;/p&gt;
&lt;p&gt;In the case of Tremor, we wanted to make it possible to configure the directories where the plugins may be saved. Thus, I introduced a new environment variable &lt;code&gt;TREMOR_PLUGIN_PATH&lt;/code&gt;. It’s equivalent to &lt;code&gt;PATH&lt;/code&gt; in the sense that the directories are separated by colons.&lt;/p&gt;
&lt;p&gt;Once the runtime has a list of what directories may contain plugins, it can look for them recursively with the crate &lt;a href=&quot;https://crates.io/crates/walkdir&quot;&gt;&lt;code&gt;walkdir&lt;/code&gt;&lt;/a&gt;. It’s highly customizable and remarkably easy to use:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/// Recursively finds all the connector plugins in a directory. It doesn&#39;t&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/// follow symlinks, and has a sensible maximum depth so that it doesn&#39;t get&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/// stuck.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;find_recursively&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;base_dir&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ConnectorMod_Ref&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;WalkDir&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;base_dir&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// No symlinks are followed for now&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;follow_links&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Adding some safe limits&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;max_depth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;into_iter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Ignoring permission errors&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter_map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;ok&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Only try to load those that look like plugins on the current platform&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token closure-params&quot;&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;file&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;extension&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token closure-params&quot;&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;ext&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;/span&gt; ext &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;env&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;consts&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;DLL_EXTENSION&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
                &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap_or&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Try to load the plugins and if successful, add them to the result.&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Not being able to load a plugin shouldn&#39;t be fatal because it&#39;s very&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// likely in some situations. Errors will just be printed to the logs.&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;filter_map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token closure-params&quot;&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;file&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;match&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConnectorMod_Ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;load_from_file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;plugin&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;plugin&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            &lt;span class=&quot;token class-name&quot;&gt;Err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                &lt;span class=&quot;token namespace&quot;&gt;log&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token macro property&quot;&gt;debug!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Failed to load plugin in &#39;{:?}&#39;: {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; file&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
                &lt;span class=&quot;token class-name&quot;&gt;None&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;collect&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a name=&quot;_separating_runtime_and_interface&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;separating-runtime-and-interface&quot; tabindex=&quot;-1&quot;&gt;Separating runtime and interface &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-impl/#separating-runtime-and-interface&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;So far I’ve been assuming the following (ideal) structure for the plugin system:&lt;/p&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/blog/plugin-impl/Oe82SZhn2c-473.avif 473w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/blog/plugin-impl/Oe82SZhn2c-473.webp 473w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://nullderef.com/blog/plugin-impl/Oe82SZhn2c-473.png&quot; width=&quot;473&quot; alt=&quot;Architecture diagram&quot; height=&quot;383&quot;&gt;&lt;/picture&gt;
&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;runtime&lt;/code&gt; crate, which loads and runs the plugins.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;common&lt;/code&gt; crate, with the interface shared between the plugin and the runtime.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;plugin&lt;/code&gt; crates, with the loadable functionality.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This is essential in order to actually improve compilation times, which is one of the main objectives of our plugin system. There are two ways to look at compilation times:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;For &lt;strong&gt;runtime&lt;/strong&gt; development&lt;/li&gt;
&lt;li&gt;For &lt;strong&gt;plugin&lt;/strong&gt; development&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In both cases, we want to compile &lt;em&gt;only&lt;/em&gt; either component. If we’re developing a plugin, it makes no sense to be forced to compile the runtime as well, because we aren’t actually changing it. And if we’re working on the runtime, we don’t want to re-compile the functionality from the plugins.&lt;/p&gt;
&lt;p&gt;If we just separate the runtime from the plugins, we can achieve the first point. The functionality is now in separate plugin binaries, so the runtime will have considerably lower compilation times.&lt;/p&gt;
&lt;p&gt;But we also want to improve plugin compilation times, and if the interface is in the same crate as the runtime, we’ll have to compile both. The shared parts should be written in a separate crate.&lt;/p&gt;
&lt;p&gt;The problem is that if you aren’t starting from scratch, it’s very likely that you won’t have a &lt;code&gt;common&lt;/code&gt; crate. You’ll just have a single binary crate with everything: &lt;code&gt;runtime&lt;/code&gt;, &lt;code&gt;plugin&lt;/code&gt;s, and &lt;code&gt;common&lt;/code&gt;. This is exactly what happened to me with Tremor as well.&lt;/p&gt;
&lt;p&gt;For now, I just have the runtime and the interface in the same crate. External plugins then have the entire runtime as a dependency. Which, as I said, is suboptimal, but it works. Moving so much stuff into a separate &lt;code&gt;common&lt;/code&gt; crate would mean a &lt;em&gt;ton&lt;/em&gt; of conflicts, so I would rather leave it for whenever the first iteration is merged. Here’s the “just make it work” structure for the plugin system:&lt;/p&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/blog/plugin-impl/jl0rsdtgAw-297.avif 297w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/blog/plugin-impl/jl0rsdtgAw-297.webp 297w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://nullderef.com/blog/plugin-impl/jl0rsdtgAw-297.png&quot; width=&quot;297&quot; alt=&quot;Architecture diagram&quot; height=&quot;343&quot;&gt;&lt;/picture&gt;
&lt;p&gt;&lt;a name=&quot;schedule&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;getting-closer-to-deployment&quot; tabindex=&quot;-1&quot;&gt;Getting closer to deployment &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-impl/#getting-closer-to-deployment&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Preparing the release for the plugin system has always sounded like a quite complicated task. I’ve never wanted to introduce too many changes at once because otherwise it will become very hard to review and merge into the main branch. This is why I decided to schedule the release in four separate iterations:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Define the new interface and use it internally&lt;/strong&gt;: the plugin system should be as minimal as possible. The program can be converted to support plugins, but while keeping them in the same binary, for simplicity. The plugin loading functionality can be left as a proof-of-concept for now. Still, this release could maybe include a couple external plugins for testing purposes.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Separate the runtime from the interface&lt;/strong&gt;: what I talked about in the previous section can actually be done now. Plugins should be able to depend &lt;em&gt;only&lt;/em&gt; on the &lt;code&gt;common&lt;/code&gt; crate, rather than on the runtime. We may also run into many Git conflicts while doing this, but it should be a much faster process, since the crate separation is all we have to worry about now.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Actually make the plugins external&lt;/strong&gt;: since the in-tree plugins are implemented with the new interface, making them actually external should be trivial. It’ll just require re-organizing the repository with the new crates, fixing the build system, and similars.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Polishing for deployment&lt;/strong&gt;: last improvements before the release, extensive documentation, evaluating the final results, etc.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Step 2 and 3 can be interchanged as desired, but I thought that the sooner we separate the crates, the better. Plugin development should be much faster after that step for everyone else.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;conclusion&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-impl/#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This has been my road to implementing the first version of Tremor’s plugin system. I’m still only on the first step of the &lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#schedule&quot;&gt;schedule&lt;/a&gt;, but hey, it works! The next and hopefully last article will cover the final cleaning up, testing and benchmarking required to make it ready for deployment, hopefully in v0.12 :)&lt;/p&gt;
&lt;p&gt;I will also work on properly making &lt;code&gt;Value&lt;/code&gt; &lt;code&gt;#[repr(C)]&lt;/code&gt;, instead of also having &lt;code&gt;PdkValue&lt;/code&gt;. In retrospect, creating &lt;code&gt;PdkValue&lt;/code&gt; was a great decision at that point: these 120 errors I got when trying to make &lt;code&gt;Value&lt;/code&gt; &lt;code&gt;#[repr(C)]&lt;/code&gt; were related to a nasty bug in &lt;code&gt;RCow&lt;/code&gt;’s implementation. Changing from &lt;code&gt;Cow&lt;/code&gt; to &lt;code&gt;RCow&lt;/code&gt; is broken in some cases because &lt;code&gt;RCow&lt;/code&gt; is &lt;em&gt;invariant&lt;/em&gt;. For those that don’t know what that means, don’t worry, as I will be releasing another article that explains everything once it’s been fixed. If you’re interested, you can follow this issue in the meanwhile, and hope that you don’t run into it:&lt;/p&gt;
&lt;p style=&quot;text-align:center&quot;&gt;
  &lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&amp;nbsp;&lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rodrimati1992/abi_stable_crates/issues/75&quot;&gt;lifetimes with R* types break compared to non R* types &lt;span class=&quot;gh-number&quot;&gt;rodrimati1992/abi_stable_crates#75&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;After I’m fully done I will also reorganize this series a bit and make it an easier read. I have been writing these articles &lt;em&gt;as I learned how the plugin system could be implemented&lt;/em&gt;, so there might be some outdated or repetitive statements in previous articles. If you have any suggestions you can leave them here:&lt;/p&gt;
&lt;p style=&quot;text-align:center&quot;&gt;
  &lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&amp;nbsp;&lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/marioortizmanero/nullderef.com/issues/50&quot;&gt;Reorganize rust plugins series once I&#39;m done &lt;span class=&quot;gh-number&quot;&gt;marioortizmanero/nullderef.com#50&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;For those interested, I recently gave a quick talk about the whole project in the 2022 LFX Mentorship Showcase. Unfortunately, it was just a 15 minutes presentation, so I couldn’t get into many technical details, but it covers how the whole experience has been so far, and what I’ve learned:&lt;/p&gt;
&lt;iframe width=&quot;1600&quot; height=&quot;400&quot; src=&quot;https://www.youtube.com/embed/htLCyqY0kt0?start=3166&quot; title=&quot;YouTube video player&quot; frameborder=&quot;0&quot; allow=&quot;accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture&quot; allowfullscreen=&quot;&quot;&gt;&lt;/iframe&gt;

&lt;script src=&quot;https://f.convertkit.com/ckjs/ck.5.js&quot;&gt;&lt;/script&gt;
&lt;form action=&quot;https://app.kit.com/forms/7534343/subscriptions&quot; class=&quot;seva-form formkit-form&quot; method=&quot;post&quot; data-sv-form=&quot;7534343&quot; data-uid=&quot;3680381778&quot; data-format=&quot;inline&quot; data-version=&quot;5&quot; data-options=&#39;{&quot;settings&quot;:{&quot;after_subscribe&quot;:{&quot;action&quot;:&quot;message&quot;,&quot;success_message&quot;:&quot;Success! Check your email to confirm your subscription. If you can’t find it, look in the spam folder.&quot;,&quot;redirect_url&quot;:&quot;&quot;},&quot;analytics&quot;:{&quot;google&quot;:null,&quot;fathom&quot;:null,&quot;facebook&quot;:null,&quot;segment&quot;:null,&quot;pinterest&quot;:null,&quot;sparkloop&quot;:null,&quot;googletagmanager&quot;:null},&quot;modal&quot;:{&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;powered_by&quot;:{&quot;show&quot;:true,&quot;url&quot;:&quot;https://kit.com/features/forms?utm_campaign=poweredby&amp;amp;utm_content=form&amp;amp;utm_medium=referral&amp;amp;utm_source=dynamic&quot;},&quot;recaptcha&quot;:{&quot;enabled&quot;:false},&quot;return_visitor&quot;:{&quot;action&quot;:&quot;show&quot;,&quot;custom_content&quot;:&quot;&quot;},&quot;slide_in&quot;:{&quot;display_in&quot;:&quot;bottom_right&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;sticky_bar&quot;:{&quot;display_in&quot;:&quot;top&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15}},&quot;version&quot;:&quot;5&quot;}&#39; min-width=&quot;400 500 600 700 800&quot;&gt;
  &lt;div data-style=&quot;clean&quot;&gt;
    &lt;div class=&quot;formkit-header&quot; data-element=&quot;header&quot;&gt;
      &lt;h2&gt;Want to receive an email when I post?&lt;/h2&gt;
    &lt;/div&gt;
    &lt;ul class=&quot;formkit-alert formkit-alert-error&quot; data-element=&quot;errors&quot; data-group=&quot;alert&quot;&gt;&lt;/ul&gt;
    &lt;div data-element=&quot;fields&quot; data-stacked=&quot;false&quot; class=&quot;seva-fields formkit-fields&quot;&gt;
      &lt;div class=&quot;formkit-field&quot;&gt;
        &lt;input class=&quot;formkit-input&quot; name=&quot;email_address&quot; aria-label=&quot;Email Address&quot; placeholder=&quot;Email Address&quot; required=&quot;&quot; type=&quot;email&quot;&gt;
      &lt;/div&gt;
      &lt;button data-element=&quot;submit&quot; class=&quot;formkit-submit&quot; style=&quot;background-color:#e06c75;font-weight:700&quot;&gt;
        &lt;div class=&quot;formkit-spinner&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span class=&quot;&quot;&gt;Subscribe&lt;/span&gt;
      &lt;/button&gt;
    &lt;/div&gt;
    &lt;div class=&quot;formkit-guarantee&quot; data-element=&quot;guarantee&quot;&gt;
      &lt;p&gt;&lt;em&gt;Free and without spam. You can also use my &lt;a href=&quot;https://nullderef.com/index.xml&quot; target=&quot;_blank&quot;&gt;RSS&lt;/a&gt;, &lt;a href=&quot;https://x.com/null_deref&quot; target=&quot;_blank&quot;&gt;X&lt;/a&gt;, or &lt;a href=&quot;https://mstdn.social/@nullderef&quot; target=&quot;_blank&quot;&gt;Mastodon&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
      &lt;p style=&quot;opacity:.6;font-size:16px&quot;&gt;&lt;em&gt;Powered by &lt;a href=&quot;https://kit.com/&quot; target=&quot;_black&quot;&gt;Kit&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;style&gt;.formkit-form[data-uid=&quot;3680381778&quot;] a{text-decoration:underline}.formkit-form[data-uid=&quot;3680381778&quot;] legend{border:none;font-size:inherit;margin-bottom:10px;padding:0;position:relative;display:table}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-checkboxes,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-select{width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{border:0;border-radius:var(--radius);color:#fff;cursor:pointer;display:inline-block;text-align:center;font-weight:500;cursor:pointer;margin-bottom:15px;overflow:hidden;padding:0;position:relative;vertical-align:middle}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover{outline:0}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover&gt;span{background-color:rgba(0,0,0,.1)}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit&gt;span{display:block;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out;padding:12px 24px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input{background:#fff;padding:12px;border:1px solid var(--border);border-radius:var(--radius);-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;line-height:1.4;margin:0;-webkit-transition:border-color ease-out .3s;transition:border-color ease-out .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:focus{outline:0;border-color:#1677be;-webkit-transition:border-color ease .3s;transition:border-color ease .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-webkit-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-moz-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:-ms-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert{background:#f9fafb;border:1px solid #e3e3e3;border-radius:5px;-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;list-style:none;margin:25px auto;padding:12px;text-align:center;width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert:empty{display:none}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-success{background:#d3fbeb;border-color:#10bf7a;color:#0c905c}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-error{background:#fde8e2;border-color:#f2643b;color:#ea4110}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;height:0;width:0;margin:0 auto;position:absolute;top:0;left:0;right:0;width:0;overflow:hidden;text-align:center;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div{margin:auto;width:12px;height:12px;background-color:#fff;opacity:.3;border-radius:100%;display:inline-block;-webkit-animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both;animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:first-child{-webkit-animation-delay:-.32s;animation-delay:-.32s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:nth-child(2){-webkit-animation-delay:-.16s;animation-delay:-.16s}.formkit-form[data-uid=&quot;3680381778&quot;]{border:1px solid var(--border);border-radius:var(--radius);position:relative;overflow:hidden;background:var(--code-bg)}.formkit-form[data-uid=&quot;3680381778&quot;]{padding:20px;width:100%;position:relative}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-header{margin:0 0 27px 0;font-size:20px;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee{margin-top:10px;margin-bottom:32px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee&gt;p{margin:0;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-fields{display:flex;flex-wrap:wrap;margin:25px auto 0 auto;gap:10px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-field,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{flex:1 0 100%}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;]{padding:0 30px}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-field{flex:100 1 auto}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-submit{flex:1 1 auto}&lt;/style&gt;
&lt;/form&gt;
&lt;h2 id=&quot;footnotes&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rust-lang/rust/pull/58623&quot;&gt;Replace HashMap implementation with SwissTable (as an external crate) &lt;span class=&quot;gh-number&quot;&gt;rust-lang/rust#58623&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/tremor-rs/tremor-runtime/blob/87fb312395b8241e915f626dd3ac3a294515e8db/tremor-value/src/known_key.rs&quot;&gt;JSON Key memoization in &lt;code&gt;tremor-runtime/tremor-value/src/known_key.rs&lt;/code&gt;&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-impl/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</content>
  </entry>
  <entry>
    <title>Plugins in Rust: Reducing the Pain with Dependencies</title>
    <link href="https://nullderef.com/blog/plugin-abi-stable/" />
    <updated>2021-11-08T00:00:00Z</updated>
    <id>https://nullderef.com/blog/plugin-abi-stable/</id>
    <content type="html">&lt;details class=&quot;toc&quot;&gt;
&lt;summary&gt;Table of Contents&lt;/summary&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#handy-tools-for-our-plugin-system&quot;&gt;Handy tools for our Plugin System &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#async-with-the-c-abi&quot;&gt;Async with the C ABI &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#lccc&quot;&gt;LCCC &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#safer-ffi&quot;&gt;Safer FFI &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#cglue&quot;&gt;CGlue &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#miri&quot;&gt;Miri &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#cbindgen&quot;&gt;cbindgen &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#working-with-abi-stable&quot;&gt;Working with abi_stable &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#versioning&quot;&gt;Versioning &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#loading-plugins&quot;&gt;Loading plugins &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#handling-state&quot;&gt;Handling state &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#regular-rust&quot;&gt;Regular Rust &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#interface-types&quot;&gt;Interface types &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#trait-objects&quot;&gt;Trait objects &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#error-handling&quot;&gt;Error handling &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#version-mismatch&quot;&gt;Version mismatch &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#missing-fields-and-wrong-types&quot;&gt;Missing fields and wrong types &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#panicking&quot;&gt;Panicking &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#panicking-and-ffi&quot;&gt;Panicking and FFI &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#aborting&quot;&gt;Aborting &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#c-unwind&quot;&gt;C-unwind &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#abortbomb&quot;&gt;AbortBomb &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#recovering-with-catch-unwind&quot;&gt;Recovering with catch_unwind &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#type-conversions&quot;&gt;Type conversions &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#thread-safety&quot;&gt;Thread safety &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#performance&quot;&gt;Performance &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#conclusion&quot;&gt;Conclusion &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/details&gt;
&lt;p&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/&quot;&gt;Previously&lt;/a&gt; in this &lt;a href=&quot;https://nullderef.com/series/rust-plugins/&quot;&gt;series&lt;/a&gt;, I covered how the plugin system could be implemented from scratch. This is a lot of work if you’re dealing with a relatively large codebase and therefore a complex interface in your plugin system, so let’s see how we can make our lives easier. I’ve been wanting to try &lt;a href=&quot;https://crates.io/crates/abi_stable&quot;&gt;&lt;code&gt;abi_stable&lt;/code&gt;&lt;/a&gt; for this since the beginning, which was specifically created for plugins. But we aren’t really locked to that crate, so I’ll show other alternatives as well, which can even be combined to your liking.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_handy_tools_for_our_plugin_system&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;handy-tools-for-our-plugin-system&quot; tabindex=&quot;-1&quot;&gt;Handy tools for our Plugin System &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#handy-tools-for-our-plugin-system&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a name=&quot;_async_with_the_c_abi&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;async-with-the-c-abi&quot; tabindex=&quot;-1&quot;&gt;Async with the C ABI &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#async-with-the-c-abi&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In a previous post I mentioned that async was not supported in &lt;code&gt;abi_stable&lt;/code&gt;. While this is true, because there is no FFI-safe &lt;code&gt;Future&lt;/code&gt; in the crate, it’s certainly possible, and it might be of interest later on.&lt;/p&gt;
&lt;p&gt;Matthias recently let me know about the &lt;a href=&quot;https://crates.io/crates/async_ffi&quot;&gt;&lt;code&gt;async_ffi&lt;/code&gt;&lt;/a&gt; crate, which lets us do exactly that. It exports the type &lt;code&gt;FfiFuture&amp;lt;T&amp;gt;&lt;/code&gt;, which provides the same functionality as &lt;code&gt;Box&amp;lt;dyn Future&amp;lt;Output = T&amp;gt; + Send&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// This is how regular async works: the first function is practically equivalent&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// to the second.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;read_file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Future&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Output&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;read_file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// For FFI-safe interfaces there can&#39;t be generics involved, so the future is a&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// concrete type instead of a trait. This conversion from `Future` to&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// `FfiFuture` can be done with `into_ffi`.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;FfiFuture&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;move&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token function&quot;&gt;read_file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;into_ffi&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// `FfiFuture&amp;lt;T&gt;` implements `Future&amp;lt;Output = T&gt;`, so it can be awaited as usual&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;async&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;example&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;await&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rodrimati1992/abi_stable_crates/issues/25&quot;&gt;Someone asked for this feature in &lt;code&gt;abi_stable&lt;/code&gt; back in 2019&lt;/a&gt;, but noone seemed interested enough to implement it at that time, so maybe in the future.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_lccc&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;lccc&quot; tabindex=&quot;-1&quot;&gt;LCCC &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#lccc&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/LightningCreations/lccc&quot;&gt;&lt;em&gt;Lightning Creations Compiler Collection&lt;/em&gt;&lt;/a&gt; provides a set of frontends and backends with a uniform intermediate representation for multiple programming languages, &lt;a href=&quot;https://github.com/LightningCreations/lccc/tree/riir/xlang/xlang_abi/src/&quot;&gt;including Rust&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;This means that they’ve written their own standard library with the C ABI, which is exactly what we need. It’s much simpler than Rust’s standard library, but it includes the most popular types your library may use: &lt;code&gt;HashMap&lt;/code&gt;, &lt;code&gt;Vec&lt;/code&gt;, &lt;code&gt;String&lt;/code&gt;, &lt;code&gt;Box&lt;/code&gt;, etc. The source code is quite nice to read in comparison to &lt;code&gt;std&lt;/code&gt;, which often includes lots of procedural macros and various forms of astral magic.&lt;/p&gt;
&lt;p&gt;It’s not too popular right now, and it’s still Work In Progress, but it serves as an example of what we’re looking for in this article. We just want to simplify our lives by having a &lt;code&gt;#[repr(C)]&lt;/code&gt;-compatible standard library so that we don’t have to write it ourselves. If all you need is something simple like LCCC, consider this library or a similar one.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_safer_ffi&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;safer-ffi&quot; tabindex=&quot;-1&quot;&gt;Safer FFI &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#safer-ffi&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If you don’t like any of the solutions listed in this article, and you’re going to end up writing the plugin interfaces by hand, you might be interested in &lt;a href=&quot;https://crates.io/crates/safer_ffi&quot;&gt;&lt;code&gt;safer_ffi&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;All this crate provides is a set of procedural macros to make FFI interfacing an easier and safer task. With it, you’ll be able to get rid of lots of &lt;code&gt;extern &amp;quot;C&amp;quot;&lt;/code&gt; and &lt;code&gt;unsafe&lt;/code&gt; instances in your code, which can get out of hands in larger codebases. Its documentation is excellent, you can check out &lt;a href=&quot;https://getditto.github.io/safer_ffi/&quot;&gt;its book&lt;/a&gt; for more information.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_cglue&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;cglue&quot; tabindex=&quot;-1&quot;&gt;CGlue &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#cglue&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;

&lt;p&gt;In my last post, I was brought up the &lt;a href=&quot;https://crates.io/crates/cglue&quot;&gt;&lt;code&gt;cglue&lt;/code&gt;&lt;/a&gt; crate &lt;a href=&quot;https://www.reddit.com/r/rust/comments/q2n6b8/plugins_in_rust_diving_into_dynamic_loading/hfmyn6o/&quot;&gt;by its own creator&lt;/a&gt;. It takes a very interesting approach, achieving ABI stability through &lt;a href=&quot;https://en.wikipedia.org/wiki/Opaque_data_type&quot;&gt;&lt;em&gt;opaque types&lt;/em&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;An opaque type is simply one for which you don’t know its concrete layout. There’s no &lt;code&gt;#[repr(C)]&lt;/code&gt; needed at all, because one can only interact with it via void pointers and its associated vtables.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;cglue&lt;/code&gt;’s README showcases the following snippet of code, and the repo even includes an &lt;a href=&quot;https://github.com/h33p/cglue/tree/main/examples&quot;&gt;example of a plugin system&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;cglue&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// One annotation for the trait.&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#[cglue_trait]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;trait&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;InfoPrinter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;print_info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Info&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;usize&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InfoPrinter&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Info&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;print_info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Info struct: {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;use_info_printer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;printer&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InfoPrinter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Printing info:&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    printer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;print_info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; info &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Info&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        value&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;5&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Here, the object is fully opaque, and is FFI and ABI safe.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; obj &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token macro property&quot;&gt;trait_obj!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; info &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;InfoPrinter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;use_info_printer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;cglue&lt;/code&gt; is limited to just generating FFI-safe trait objects, trying to make the whole process as straightforward as possible. You could say that &lt;code&gt;cglue&lt;/code&gt; covers just a subset of what &lt;code&gt;abi_stable&lt;/code&gt; does, because most of this is also available in &lt;code&gt;abi_stable&lt;/code&gt; through the &lt;code&gt;sabi_trait&lt;/code&gt; procedural macro, which I’ll &lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#sabi_trait&quot;&gt;explain later&lt;/a&gt;. It’s possible to combine both crates, which is something &lt;code&gt;cglue&lt;/code&gt; plans to do in the future. &lt;code&gt;cglue&lt;/code&gt; offers the following benefits over &lt;code&gt;sabi_trait&lt;/code&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It’s possible to generate bindings for C/C++, which means that plugins can be written in languages other than Rust.&lt;/li&gt;
&lt;li&gt;You can define &lt;em&gt;trait groups&lt;/em&gt;, even with optional traits.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Neither of these are particularly useful for my use-case, but if any of these features interests you, definitely take a deeper look. It’s actively maintained and constantly being improved; the documentation is great and the author frequently uploads updates to his &lt;a href=&quot;https://blaz.is/&quot;&gt;personal blog&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_miri&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;miri&quot; tabindex=&quot;-1&quot;&gt;Miri &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#miri&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rust-lang/miri&quot;&gt;Miri&lt;/a&gt; is an interpreter for Rust’s mid-level intermediate representation. This doesn’t help us with the plugin system per se, but since it’s very likely that we’re going to end up writing unsafe code, it’s good to know about it. That’s exactly what Miri is used for: detecting undefined behavior, such as using uninitialized data or use-after-frees.&lt;/p&gt;
&lt;p&gt;I was going to use Miri from the beginning, but since I’ll be using &lt;a href=&quot;https://crates.io/crates/abi_stable&quot;&gt;&lt;code&gt;abi_stable&lt;/code&gt;&lt;/a&gt; for now, there will be no unsafe code involved. If I end up having to resort to it, I’ll try to add Miri to Tremor’s workflow (mainly their Continuous Integration).&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_cbindgen&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;cbindgen&quot; tabindex=&quot;-1&quot;&gt;cbindgen &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#cbindgen&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;For the first steps with dynamic loading I think the C/C++ binding generator &lt;a href=&quot;https://crates.io/crates/cbindgen&quot;&gt;&lt;code&gt;cbindgen&lt;/code&gt;&lt;/a&gt; will help us understand what’s going on under the hood. We can take a look at the generated headers and see how it works internally. Unfortunately, it fails to run for the &lt;code&gt;abi_stable&lt;/code&gt; crate:&lt;/p&gt;
&lt;pre class=&quot;language-plain&quot;&gt;&lt;code class=&quot;language-plain&quot;&gt;(...)
WARN: Skip abi_stable::CONST - (...)

thread &#39;main&#39; panicked at &#39;RResult has 2 params but is being instantiated with 1 values&#39;, src/bindgen/ir/enumeration.rs:596:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This &lt;em&gt;probably&lt;/em&gt; has to do with the following warning found in &lt;a href=&quot;https://github.com/eqrion/cbindgen/blob/master/docs.md&quot;&gt;&lt;code&gt;cbindgen&lt;/code&gt;’s documentation&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;NOTE: A major limitation of cbindgen is that it does not understand Rust’s module system or namespacing. This means that if cbindgen sees that it needs the definition for &lt;code&gt;MyType&lt;/code&gt; and there exists two things in your project with the type name &lt;code&gt;MyType&lt;/code&gt;, it won’t know what to do. Currently, cbindgen’s behaviour is unspecified if this happens. However, this may be ok if they have &lt;a href=&quot;https://github.com/eqrion/cbindgen/blob/master/docs.md#defines-and-cfgs&quot;&gt;different cfgs&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;If you’re using something else like &lt;code&gt;cglue&lt;/code&gt;, this will work without issues. But after letting the maintainers of &lt;code&gt;abi_stable&lt;/code&gt; know about this in &lt;a href=&quot;https://github.com/rodrimati1992/abi_stable_crates/issues/52&quot;&gt;an issue&lt;/a&gt;, they pointed out that this was expected and that they don’t plan on supporting &lt;code&gt;cbindgen&lt;/code&gt; because it would take too much effort. Understandable, so let’s move on.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_working_with_abi_stable&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;working-with-abi-stable&quot; tabindex=&quot;-1&quot;&gt;Working with &lt;code&gt;abi_stable&lt;/code&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#working-with-abi-stable&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I will personally use &lt;a href=&quot;https://crates.io/crates/abi_stable&quot;&gt;&lt;code&gt;abi_stable&lt;/code&gt;&lt;/a&gt; because it seems like the easiest choice for now, and the one that meets my needs best. Not only does it provide a standard library defined with the C ABI, but also lots of other macros and utilities specially useful for plugin systems. With it, I won’t need a line of unsafe, and I’ll avoid reinventing the wheel in many instances.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Once the plugin system is fully functional with &lt;code&gt;abi_stable&lt;/code&gt;&lt;/em&gt;, I might consider using something more hand-crafted. This switch won’t be too complicated, since our interface will already be &lt;code&gt;#[repr(C)]&lt;/code&gt;, which is the most troublesome part. All we’d have to do is remove a few procedural macros, switch the &lt;code&gt;abi_stable&lt;/code&gt; types, and load the plugins manually with something like &lt;a href=&quot;https://crates.io/crates/libloading&quot;&gt;&lt;code&gt;libloading&lt;/code&gt;&lt;/a&gt;. The only thing I want right now is a plugin system that works, and then we can maybe focus on trying to make it available in other languages, making it more performant, or whatever.&lt;/p&gt;
&lt;p&gt;So let’s start comparing &lt;code&gt;abi_stable&lt;/code&gt; with my experiments in the previous post using raw dynamic linking. I’ve created the &lt;code&gt;abi-stable-simple&lt;/code&gt; directory &lt;a href=&quot;https://github.com/marioortizmanero/pdk-experiments&quot;&gt;in the pdk-experiments repository&lt;/a&gt;. I’ll be taking a look at the already implemented &lt;a href=&quot;https://github.com/rodrimati1992/abi_stable_crates/tree/master/examples&quot;&gt;examples&lt;/a&gt; for &lt;code&gt;abi_stable&lt;/code&gt; in order to make the learning experience smoother. The base structure for a plugin system with &lt;code&gt;abi_stable&lt;/code&gt; is the same as always: a crate for the plugin, another for the runtime, and &lt;code&gt;common&lt;/code&gt;, with the shared interface.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_versioning&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;versioning&quot; tabindex=&quot;-1&quot;&gt;Versioning &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#versioning&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;abi_stable&lt;/code&gt; &lt;a href=&quot;https://github.com/rodrimati1992/abi_stable_crates#safety&quot;&gt;states this&lt;/a&gt; regarding versioning:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;This library ensures that the loaded libraries are safe to use through these mechanisms:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The abi_stable ABI of the library is checked, Each &lt;code&gt;0.y.0&lt;/code&gt; version and &lt;code&gt;x.0.0&lt;/code&gt; version of abi_stable defines its own ABI which is incompatible with previous versions.&lt;/li&gt;
&lt;li&gt;Types are recursively checked when the dynamic library is loaded, before any function can be called.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;In summary, &lt;code&gt;abi_stable&lt;/code&gt; itself is far from being permanently backward compatible, but it automatically makes sure that its versions are compatible when running the plugin. While it doesn’t exactly stick to semantic versioning, it’s good enough for us.&lt;/p&gt;
&lt;p&gt;The version checking for the entire &lt;code&gt;common&lt;/code&gt; crate is already implemented, i.e., we can’t try to mix different versions that aren’t compatible. We could still add a version string for each kind of plugin if more fine-grained control is needed, as described in the previous post.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_loading_lugins&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;loading-plugins&quot; tabindex=&quot;-1&quot;&gt;Loading plugins &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#loading-plugins&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;abi_stable&lt;/code&gt; plugins are structured in &lt;em&gt;modules&lt;/em&gt;, which can help us split up our functionality into smaller independent pieces. There must always be a &lt;a href=&quot;https://docs.rs/abi_stable/latest/abi_stable/library/trait.RootModule.html&quot;&gt;root module&lt;/a&gt; that initializes the entire library and provides metadata such as the name or the version strings. Then, we can have submodules to organize the functions exported by the library nicely.&lt;/p&gt;
&lt;p&gt;Furthermore, the &lt;a href=&quot;https://docs.rs/abi_stable/latest/abi_stable/abi_stability/stable_abi_trait/trait.StableAbi.html&quot;&gt;&lt;code&gt;StableAbi&lt;/code&gt;&lt;/a&gt; trait in &lt;code&gt;abi_stable&lt;/code&gt; indicates that a type is FFI-safe. It contains information about the layout of the type, and it can be &lt;a href=&quot;https://docs.rs/abi_stable/latest/abi_stable/derive.StableAbi.html&quot;&gt;derived automatically&lt;/a&gt;. Each item in &lt;code&gt;abi_stable&lt;/code&gt;’s standard library (&lt;code&gt;RStr&lt;/code&gt;, &lt;code&gt;RSlice&amp;lt;T&amp;gt;&lt;/code&gt;, &lt;code&gt;RArc&amp;lt;T&amp;gt;&lt;/code&gt;, etc) implements this trait, and it’s used to make sure the types are compatible when loading the plugin.&lt;/p&gt;
&lt;p&gt;This also introduces the concept of &lt;a href=&quot;https://docs.rs/abi_stable/latest/abi_stable/docs/prefix_types/index.html&quot;&gt;&lt;em&gt;prefix types&lt;/em&gt;&lt;/a&gt;. When a type derives &lt;code&gt;StableAbi&lt;/code&gt; and has the &lt;code&gt;#[sabi(kind(Prefix(...)))]&lt;/code&gt; attribute, two more types are generated:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;name&amp;gt;_Prefix&lt;/code&gt;, which contains all the fields up to the &lt;code&gt;#[sabi(last_prefix_field)]&lt;/code&gt; attribute in the original type.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;&amp;lt;name&amp;gt;_Ref&lt;/code&gt;, which is a pointer to &lt;code&gt;&amp;lt;name&amp;gt;_Prefix&lt;/code&gt; that can actually be passed through the FFI barrier safely.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Prefix types are needed to guarantee some kind of individual versioning to avoid breakage in future patches. It will let us add more fields to the module after the &lt;code&gt;last_prefix_field&lt;/code&gt; attribute in patch (&lt;code&gt;0.0.x&lt;/code&gt;) updates. Moving this attribute requires a backward-incompatible version bump. Prefix types are often used for modules and vtables.&lt;/p&gt;
&lt;p&gt;For now, I’ll just have a single root module and call it &lt;code&gt;MinMod&lt;/code&gt;, exporting the &lt;code&gt;min&lt;/code&gt; function:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Using the stable C ABI&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#[repr(C)]&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Deriving the `StableAbi` trait, which defines the layout of the struct at&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// compile-time:&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// https://docs.rs/abi_stable/0.10.2/abi_stable/derive.StableAbi.html&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#[derive(StableAbi)]&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Marking the struct as a prefix-type:&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// https://docs.rs/abi_stable/0.10.2/abi_stable/docs/prefix_types/index.html&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#[sabi(kind(Prefix))]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;MinMod&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// Initializes the state, which will be passed to the functions in this&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// module. I&#39;ll explain more about the state later on.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; new&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;State&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;/// Calculates the minimum between two integers. This is the last defined&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// field for the current version. If we try to load fields after this, all&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// of them will be an `Option`.&lt;/span&gt;
    &lt;span class=&quot;token attribute attr-name&quot;&gt;#[sabi(last_prefix_field)]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; min&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;State&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Most of the loading functionality is already handled by &lt;code&gt;abi_stable&lt;/code&gt;. The module we’re exporting implements the &lt;code&gt;RootModule&lt;/code&gt; trait, which includes functions to load the plugin, such as &lt;a href=&quot;https://docs.rs/abi_stable/latest/abi_stable/library/trait.RootModule.html#method.load_from_file&quot;&gt;&lt;code&gt;RootModule::load_from_file&lt;/code&gt;&lt;/a&gt; or &lt;a href=&quot;https://docs.rs/abi_stable/latest/abi_stable/library/trait.RootModule.html#method.load_from_directory&quot;&gt;&lt;code&gt;RootModule::load_from_directory&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// Marking `MinMod` as the main module in this plugin. Note that `MinMod_Ref` is&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// a pointer to the prefix of `MinMod`.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RootModule&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MinMod_Ref&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// The name of the dynamic library&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;BASE_NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;min&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// The name of the library for logging and similars&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;min&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// The version of this plugin&#39;s crate&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;VERSION_STRINGS&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;VersionStrings&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token macro property&quot;&gt;package_version_strings!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Implements the `RootModule::root_module_statics` function, which is the&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// only required implementation for the `RootModule` trait.&lt;/span&gt;
    &lt;span class=&quot;token macro property&quot;&gt;declare_root_module_statics!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;MinMod_Ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When loading directories, it makes the following decisions by default (though we could change them if we wanted to):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It does so non-recursively, i.e., only checking the immediate files in the given directory.&lt;/li&gt;
&lt;li&gt;The name of the library must be the &lt;code&gt;RootModule::BASE_NAME&lt;/code&gt; in lowercase, according to the &lt;a href=&quot;https://doc.rust-lang.org/std/env/consts/index.html&quot;&gt;Operating System’s defaults&lt;/a&gt;. For example, in Linux our plugin would be &lt;code&gt;libmin.so&lt;/code&gt;, and on Windows it’d be &lt;code&gt;min.dll&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This means that we should add the following parameter to the plugin’s &lt;code&gt;Cargo.toml&lt;/code&gt; file:&lt;/p&gt;
&lt;pre class=&quot;language-toml&quot;&gt;&lt;code class=&quot;language-toml&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token table class-name&quot;&gt;lib&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# This way, the shared object will be saved as `abi_stable` prefers, for example&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;# `libmin.so`.&lt;/span&gt;
&lt;span class=&quot;token key property&quot;&gt;name&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;min&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, this is what the runtime may look like:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;run_plugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; plugin &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MinMod_Ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;load_from_directory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;as_ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Loading plugin {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MinMod_Ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;NAME&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// First we obtain the function pointer. This is not an `Option` because&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// `new` is defined before `min`, the last prefix field.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; new_fn &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; plugin&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// We initialize the plugin, obtaining a state.&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;new_fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Same for the `min` function&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; min_fn &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; plugin&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;initial state: {:?}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;  min(1, 2): {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;min_fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;  min(-10, 10): {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;min_fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;  min(2000, 2000): {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;min_fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;final state: {:?}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Executing the &lt;code&gt;plugin-sample&lt;/code&gt; implementation:&lt;/p&gt;
&lt;pre class=&quot;language-plain&quot;&gt;&lt;code class=&quot;language-plain&quot;&gt;$ make debug-sample
Loading plugin min
initial state: State { counter: 0 }
  min(1, 2): 1
  min(-10, 10): -10
  min(2000, 2000): 2000
final state: State { counter: 3 }&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a name=&quot;_handling_state&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;handling-state&quot; tabindex=&quot;-1&quot;&gt;Handling state &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#handling-state&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a name=&quot;_regular_rust&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;regular-rust&quot; tabindex=&quot;-1&quot;&gt;Regular Rust &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#regular-rust&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As we saw in the previous example, we need some kind of generic &lt;code&gt;State&lt;/code&gt; type that each plugin can implement with their own data. In regular Rust, we’d do as follows. &lt;a href=&quot;https://github.com/marioortizmanero/pdk-experiments/tree/master/generics/regular-rust&quot;&gt;See the full code here&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;trait&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;State&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Debug&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Remember that we can&#39;t use generics, so we need `dyn`, either by itself as a&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// reference, or in a box.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;StateBox&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Box&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;dyn&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;State&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;usage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StateBox&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;state debug: {:?}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a name=&quot;_interface_types&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;interface-types&quot; tabindex=&quot;-1&quot;&gt;Interface types &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#interface-types&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Unfortunately, we already know that regular &lt;code&gt;dyn&lt;/code&gt; is not FFI-safe. I covered how it’s possible to work around it with pointers, but here we can resort to &lt;code&gt;abi_stable&lt;/code&gt;’s safer and more convenient alternatives. Here’s one of them (&lt;a href=&quot;https://github.com/marioortizmanero/pdk-experiments/tree/master/generics/interface-types&quot;&gt;see the full code here&lt;/a&gt;):&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[repr(C)]&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#[derive(StableAbi)]&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// An `InterfaceType` describes which traits are required when constructing&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// `StateBox` and are then usable afterwards.&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#[sabi(impl_InterfaceType(Debug, PartialEq))]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;State&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// A trait object for `State`&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;StateBox&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;DynTrait&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;static&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RBox&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;State&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// It can then be used easily like this&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;usage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StateBox&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;state debug: {:?}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we first declare a &lt;code&gt;State&lt;/code&gt; &lt;a href=&quot;https://docs.rs/abi_stable/latest/abi_stable/trait.InterfaceType.html&quot;&gt;&lt;em&gt;interface type&lt;/em&gt;&lt;/a&gt;. Note that even though it’s defined as a &lt;code&gt;struct&lt;/code&gt;, this is a translation of the previous snippet of code, so it acts as the empty “trait”. But all it does is establish &lt;code&gt;Debug&lt;/code&gt; and &lt;code&gt;PartialEq&lt;/code&gt; as its supertraits and give access to them; you can’t really add custom methods to the trait.&lt;/p&gt;
&lt;p&gt;Unlike &lt;code&gt;dyn&lt;/code&gt;, this even works with supertraits that aren’t object-safe. Thus, we can use something like &lt;code&gt;PartialEq&lt;/code&gt;. Its main disadvantage is that it’s limited to a set of 21 hardcoded traits, so it might not be enough for us.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;sabi_trait&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;trait-objects&quot; tabindex=&quot;-1&quot;&gt;Trait objects &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#trait-objects&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;If we want something more akin to traits on Rust, we can use &lt;a href=&quot;https://docs.rs/abi_stable/latest/abi_stable/attr.sabi_trait.html&quot;&gt;&lt;code&gt;#[sabi_trait]&lt;/code&gt;&lt;/a&gt;. The trait has to be object-safe, and by default there’s no support for &lt;code&gt;PartialEq&lt;/code&gt; in the list of supertraits, so I’ll remove it. &lt;a href=&quot;https://github.com/marioortizmanero/pdk-experiments/tree/master/generics/sabi-trait&quot;&gt;See the full code here&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[sabi_trait]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;trait&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;State&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Debug&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// A trait object for the `State` Trait Object&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;StateBox&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;State_TO&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;static&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RBox&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// It can then be used easily like this&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;usage&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;StateBox&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;state debug: {:?}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;state counter: {:?}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;counter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As its documentation explains, this still has a limited number of possible supertraits, but at least it lets us require functions as usual, and it even works with default implementations.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_error_handling&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;error-handling&quot; tabindex=&quot;-1&quot;&gt;Error handling &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#error-handling&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;abi_stable&lt;/code&gt; is just a wrapper over &lt;a href=&quot;https://crates.io/crates/libloading&quot;&gt;&lt;code&gt;libloading&lt;/code&gt;&lt;/a&gt; after all. It doesn’t include a sandbox, so if the plugin developer was a malicious actor, they’d have full access to the computer the runtime is being executed on. Other popular plugin systems such as &lt;a href=&quot;https://www.nginx.com/resources/wiki/extending/&quot;&gt;nginx’s&lt;/a&gt; or &lt;a href=&quot;https://httpd.apache.org/docs/2.4/dso.html&quot;&gt;apache’s&lt;/a&gt; suffer from the same issues, for reference.&lt;/p&gt;
&lt;p&gt;However, I think it’s not so bad to assume that no bad actors will be involved here. A sandbox would be mandatory if we were working on something like &lt;a href=&quot;https://solana.com/&quot;&gt;Solana&lt;/a&gt; (one of the main users of eBPF in Rust), which basically executes random code from the internet. But with Tremor we can assume that the plugins come from trusted sources because they’re installed and configured manually by the user.&lt;/p&gt;
&lt;p&gt;There are some additional security measures that could be implemented in the future, like checking the integrity of the plugins and verifying they come from a trusted source before loading them. Of course, if we could afford to have a sandbox it’d definitely be the best way to do it, but we’ve already seen in this series that it’s currently not really viable for this use-case.&lt;/p&gt;
&lt;p&gt;Still, we trust that the plugin developer has good intentions, but not necessarily that they know what they’re doing. We should make fatal errors as hard as possible to happen so that Tremor isn’t constantly crashing. The fewer pitfalls, the better.&lt;/p&gt;
&lt;p&gt;The full source for the example that’s supported to work is &lt;a href=&quot;https://github.com/marioortizmanero/pdk-experiments/tree/master/abi-stable-simple/plugin-sample&quot;&gt;here&lt;/a&gt;. Let’s see a few ways in which the plugin could go wrong:&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_version_mismatch&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;version-mismatch&quot; tabindex=&quot;-1&quot;&gt;Version mismatch &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#version-mismatch&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The versions of the &lt;code&gt;common&lt;/code&gt; library are checked automatically. In case there’s a mismatch in those considered incompatible (changes in &lt;code&gt;x.0.0&lt;/code&gt; or &lt;code&gt;0.x.0&lt;/code&gt;), this is what will show up. &lt;a href=&quot;https://github.com/marioortizmanero/pdk-experiments/tree/master/abi-stable-simple/plugin-versionmismatch&quot;&gt;See the full code here&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-plain&quot;&gt;&lt;code class=&quot;language-plain&quot;&gt;$ make debug-versionmismatch
Error when running the plugin:

(...)

Error:incompatible package versions
Expected:
    0.2.0
Found:
    0.1.0&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can absolutely catch this error gracefully and continue with the execution of the runtime, just like with raw dynamic loading. It’s even easier because it works out of the box.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_missing_fields_and_wrong_types&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;missing-fields-and-wrong-types&quot; tabindex=&quot;-1&quot;&gt;Missing fields and wrong types &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#missing-fields-and-wrong-types&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The layout of every type is recursively checked before trying to use them to make sure they are compatible. Unlike raw dynamic loading, these errors can be caught gracefully, which is a huge plus (it used to segfault). &lt;a href=&quot;https://github.com/marioortizmanero/pdk-experiments/tree/master/abi-stable-simple/plugin-wrongtype&quot;&gt;See the full code here&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-plain&quot;&gt;&lt;code class=&quot;language-plain&quot;&gt;$ make debug-wrongtype
Error when running the plugin:
Compared &amp;lt;this&gt;:
    --- Type Layout ---
    type:PrefixRef&amp;lt;&#39;a, MinMod&gt;
    (...)
To &amp;lt;other&gt;:
    --- Type Layout ---
    type:PrefixRef&amp;lt;&#39;a, MinMod&gt;
    (...)

0 error(s).

0 error(s)inside:
    &amp;lt;other&gt;

    (...)

Layout of expected type:
    --- Type Layout ---
    type:MinMod
    (...)

Layout of found type:
    --- Type Layout ---
    type:MinMod
    (...)

(...)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The error message is way too long to show here, but it basically shows the entire layout tree of the types that don’t match for each of its versions (runtime vs plugin). For this example, I changed the &lt;code&gt;State&lt;/code&gt; trait to use a boolean instead of an integer counter, and the message describes it perfectly: their sizes, alignments, and types differ in the trait’s methods.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_panicking&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;panicking&quot; tabindex=&quot;-1&quot;&gt;Panicking &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#panicking&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Panicking trough the FFI boundary is &lt;em&gt;undefined behaviour&lt;/em&gt;; we aren’t guaranteed that the plugin will abort. It may just continue its execution in a completely invalid state, which is scary. But turns out &lt;code&gt;abi_stable&lt;/code&gt; properly handles this for us! It will use what it calls an &lt;code&gt;AbortBomb&lt;/code&gt; to even print out the line and file where it happened. This is publicly available through the macro &lt;a href=&quot;https://docs.rs/abi_stable/latest/abi_stable/macro.extern_fn_panic_handling.html&quot;&gt;&lt;code&gt;extern_fn_panic_handling&lt;/code&gt;&lt;/a&gt;. &lt;a href=&quot;https://github.com/marioortizmanero/pdk-experiments/tree/master/abi-stable-simple/plugin-panic&quot;&gt;See the full code here&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-plain&quot;&gt;&lt;code class=&quot;language-plain&quot;&gt;$ make debug-panic
Loading plugin min
initial state: State { counter: 0 }
thread &#39;&amp;lt;unnamed&gt;&#39; panicked at &#39;This will crash everything&#39;, src/lib.rs:26:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

file:src/lib.rs
line:24
Attempted to panic across the ffi boundary.
Aborting to handle the panic...&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we panic in the plugin it won’t be undefined behaviour anymore because &lt;code&gt;abi_stable&lt;/code&gt; already makes sure the panic doesn’t reach the FFI boundary.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_panicking_and_ffi&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;panicking-and-ffi&quot; tabindex=&quot;-1&quot;&gt;Panicking and FFI &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#panicking-and-ffi&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As we’ve already seen, plugins cannot panic across the FFI boundary under any circumstance&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;. If we aren’t using something like &lt;code&gt;abi_stable&lt;/code&gt;, every single function we export in the plugin should wrap its contents in &lt;a href=&quot;https://doc.rust-lang.org/std/panic/fn.catch_unwind.html&quot;&gt;&lt;code&gt;catch_unwind&lt;/code&gt;&lt;/a&gt; in order to be able to panic.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Unwinding&lt;/em&gt; is a process in which all local objects are destroyed, properly calling the destructors in the thread in order to continue execution safely&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#fn4&quot; id=&quot;fnref4&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt;. Knowing this is something taken for granted when taking a look at documentation about exceptions in Rust, but it wasn’t so clear to me at the beginning.&lt;/p&gt;
&lt;p&gt;For example, the following snippet will panic after creating the vector. If panics were configured to abort, the contents of the vector wouldn’t be freed at all; the program would just end abruptly, and the cleaning up would be left to the Operating System. But if it &lt;em&gt;unwinds&lt;/em&gt;, Rust will call &lt;code&gt;Vec&lt;/code&gt;’s destructor, freeing its allocated memory properly, making it possible to continue the execution of the program.&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token macro property&quot;&gt;vec!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token macro property&quot;&gt;panic!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;oh no!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;My data: {:?}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Unreachable&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In a typical usage of Rust, a panic usually means that your program writes some scary message to stdout and then ends. This is because unwinding is propagated and it may end up finishing the execution of the program if it’s not stopped. But that’s exacty what &lt;code&gt;catch_unwind&lt;/code&gt; is for:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; result &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;panic&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch_unwind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token closure-params&quot;&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token macro property&quot;&gt;vec!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token macro property&quot;&gt;panic!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;oh no!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;My data: {:?}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Unreachable&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// This will run just fine and print out `true`&lt;/span&gt;
&lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Did it panic? {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; result&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;is_err&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Rust makes it very clear that &lt;code&gt;catch_unwind&lt;/code&gt; is not intended for regular error handling (you have &lt;code&gt;Result&lt;/code&gt; for that). But in our case we are almost forced to use it in order to not invoke undefined behaviour when panicking through the FFI boundary. Every single function in the FFI interface that has a possibility of panicking should use it so that the panic doesn’t try to propagate. And this is quite tricky because even things like addition may cause a panic (overflow in debug mode).&lt;/p&gt;
&lt;p&gt;Let’s see what else can we do about panicking:&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_aborting&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;aborting&quot; tabindex=&quot;-1&quot;&gt;Aborting &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#aborting&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The simplest way to do it would be to just configure plugins to abort on panic instead of unwinding. This is possible with the &lt;code&gt;panic = &amp;quot;abort&amp;quot; option in the plugin&#39;s &lt;/code&gt;Cargo.toml`. It will still show the panic message, but the execution will be completely stopped by an abort:&lt;/p&gt;
&lt;pre class=&quot;language-plain&quot;&gt;&lt;code class=&quot;language-plain&quot;&gt;$ cargo r -q
thread &#39;main&#39; panicked at &#39;Oh no!&#39;, src/main.rs:2:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
zsh: abort (core dumped)  cargo r -q&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is sound because the entire program’s execution ends before reaching the FFI boundary. The problem is that cleaning up will never happen, and that although there’s &lt;a href=&quot;https://stackoverflow.com/questions/51860663/is-it-possible-to-check-if-panic-is-set-to-abort-while-a-library-is-compilin&quot;&gt;a hack you can use in your &lt;code&gt;common&lt;/code&gt; library&lt;/a&gt; to make sure the plugin is compiled with `panic = “abort”, it’s only available on nightly until this is merged:&lt;/p&gt;
&lt;p style=&quot;text-align:center&quot;&gt;
  &lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&amp;nbsp;&lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rust-lang/rust/issues/32837&quot;&gt;Pluggable panic implementations (tracking issue for RFC 1513) &lt;span class=&quot;gh-number&quot;&gt;rust-lang/rust#32837&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;
&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_c_unwind&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;c-unwind&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;C-unwind&lt;/code&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#c-unwind&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This problem is something the Rust devs are aware of, and that they’re trying to fix. It has been proposed under the “C-unwind” ABI string. Just like how you currently use &lt;code&gt;extern &amp;quot;C&amp;quot;&lt;/code&gt;, if we used &lt;code&gt;extern &amp;quot;C-unwind&amp;quot;&lt;/code&gt;, we’d get more guarantees about what happens when a thread panics. More information here:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://doc.rust-lang.org/nomicon/ffi.html?highlight=panic#ffi-and-panics&quot;&gt;Current reference to FFI and panics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html&quot;&gt;RFC&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/rust-lang/rust/pull/76570&quot;&gt;Pull Request&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/rust-lang/project-ffi-unwind&quot;&gt;Project Group&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The most relevant things this feature offers us is:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Support for unwinding through the FFI boundary.&lt;/li&gt;
&lt;li&gt;A guarantee that even with &lt;code&gt;extern &amp;quot;C&amp;quot;&lt;/code&gt;, panicking is not undefined behavior, it’ll just abort (except for some very specific cases). Switching between “abort” and “unwind” for the &lt;code&gt;panic&lt;/code&gt; option in &lt;code&gt;Cargo.toml&lt;/code&gt; is always sound.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Unfortunately, it’s moving somewhat slowly, and I’m not quite sure when this will be ready. In the meanwhile, we’ll need to use something else to ensure no undefined behaviour occurs in our plugin system.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_abortbomb&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;abortbomb&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;AbortBomb&lt;/code&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#abortbomb&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;abi_stable&lt;/code&gt; does this in a pretty clever way: it creates an &lt;code&gt;AbortBomb&lt;/code&gt; struct at the beginning of the function, which contains its filename and line of code. If something panics and unwraps, &lt;code&gt;AbortBomb&lt;/code&gt;’s destructor will be called, which aborts the program. Otherwise, &lt;code&gt;mem::forget&lt;/code&gt; is called for the &lt;code&gt;AbortBomb&lt;/code&gt; at the end of the function, which will avoid calling its destructor and the function will be able to end successfully.&lt;/p&gt;
&lt;p&gt;Note that even though &lt;code&gt;mem::forget&lt;/code&gt; is called, no memory is actually being leaked, because the filename is a &lt;code&gt;&#39;static str&lt;/code&gt; — which lives for the entirety of the program — and the line number is an integer, which will be in the stack and doesn’t need fancy destructors.&lt;/p&gt;
&lt;p&gt;This approach is completely fine and works great, but it aborts the whole plugin system, so you can’t recover from it at all. In the case of Tremor, if a plugin panics, from a logical standpoint it doesn’t make much sense to continue the execution because there’s a piece missing in the pipeline. It couldn’t continue anyway… Right? Well, we could actually load the plugin that panicked again and use that instead for the remainder of the program. But since our plugin system doesn’t support unloading, we’d be leaking memory, and if the plugin keeps panicking it’d eventually crash.&lt;/p&gt;
&lt;p&gt;Recovering from a plugin panicking is definitely viable, and it might be an interesting feature for the future. Unfortunately, it’s a lot of work to make sure it works properly, and it’s not really an objective for the first implementation, so for now I’ll just use &lt;code&gt;abi_stable&lt;/code&gt;’s solution.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_recovering_with_catch_unwind&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;recovering-with-catch-unwind&quot; tabindex=&quot;-1&quot;&gt;Recovering with &lt;code&gt;catch_unwind&lt;/code&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#recovering-with-catch-unwind&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;As I explained in the beginning, &lt;code&gt;catch_unwind&lt;/code&gt; can be used to detect and stop unwinding panics. One way to notify the runtime that a plugin has panicked so that it can act accordingly would be to use an enum equivalent to &lt;code&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[repr(C)]&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#[derive(Debug, StableAbi)]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;MayPanic&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Panic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;NoPanic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;MayPanic&lt;/code&gt; is a type that only returns the original value if the function finished without panicking. Since the contents returned by &lt;code&gt;catch_unwind&lt;/code&gt; are just &lt;code&gt;dyn Any&lt;/code&gt; and don’t provide much value for us, they’re discarded and the &lt;code&gt;Panic&lt;/code&gt; variant is empty. The panicking information will be printed automatically as output anyway (or whatever is configured with &lt;a href=&quot;https://doc.rust-lang.org/std/panic/fn.set_hook.html&quot;&gt;the panic hook&lt;/a&gt;). We will use it in FFI contexts, so it also implements &lt;code&gt;StableAbi&lt;/code&gt; and it’s &lt;code&gt;#[repr(C)]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I didn’t want to use &lt;code&gt;Result&lt;/code&gt; for this because panic errors should be treated differently from a regular error. Apart from the fact that &lt;code&gt;panic::catch_unwind&lt;/code&gt; returns a &lt;code&gt;Box&amp;lt;dyn Any&amp;gt;&lt;/code&gt;, which doesn’t implement &lt;code&gt;Error&lt;/code&gt;, panics happen when the plugin reaches an unrecoverable state and cannot continue. We really have to make sure this is handled differently from a regular error, so having the type safety of a different type can help.&lt;/p&gt;
&lt;p&gt;It implements &lt;code&gt;From&amp;lt;thread::Result&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt;, so it can simply be used like this:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;plugin_stuff&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MayPanic&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Whatever&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;panic&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;catch_unwind&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token closure-params&quot;&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// Code goes here&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;into&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Ideally, &lt;code&gt;MayPanic&lt;/code&gt; could be accompanied by a &lt;code&gt;#[may_panic]&lt;/code&gt; procedural macro that adds this boilerplate automatically to the function it’s attached to. Additionally, it could come with a &lt;code&gt;#[may_not_panic]&lt;/code&gt; variant that attaches the &lt;code&gt;#[no_panic]&lt;/code&gt; macro from the &lt;a href=&quot;https://crates.io/crates/no-panic&quot;&gt;&lt;code&gt;no-panic&lt;/code&gt;&lt;/a&gt; crate to make sure the statement is true at compile time. However, &lt;code&gt;no-panic&lt;/code&gt; isn’t too reliable, so perhaps it could be opt-in with something like &lt;code&gt;#[may_not_panic(enforce)]&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Something that complicates this whole thing considerably is the concept of &lt;em&gt;exception safety&lt;/em&gt;. Unfortunately, &lt;code&gt;catch_unwind&lt;/code&gt; isn’t as easy to use as just slapping your code into its closure/function, as there are some types that aren’t considered unwind safe. You can read more about that &lt;a href=&quot;https://doc.rust-lang.org/stable/std/panic/trait.UnwindSafe.html&quot;&gt;here&lt;/a&gt;, but I won’t get into more details because we aren’t going to use &lt;code&gt;MayPanic&lt;/code&gt; in our own plugin system anyway.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_type_conversions&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;type-conversions&quot; tabindex=&quot;-1&quot;&gt;Type conversions &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#type-conversions&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It’s important to know the complexity of conversions from and to &lt;code&gt;abi_stable&lt;/code&gt; types. If &lt;code&gt;Vec&amp;lt;T&amp;gt;&lt;/code&gt; → &lt;code&gt;RVec&amp;lt;T&amp;gt;&lt;/code&gt; wasn’t $O(n)$ it might be worth avoiding it altogether.&lt;/p&gt;
&lt;p&gt;This means that I should spend at least a bit of my time on understanding how the &lt;code&gt;abi_stable&lt;/code&gt; types are implemented and making sure this isn’t the case. In &lt;code&gt;std&lt;/code&gt;, the definition of &lt;code&gt;Vec&lt;/code&gt; is actually quite simple if we remove most of the noise:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// A non-null pointer to `T` that indicates ownership.&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Unique&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Sized&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    pointer&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// The data itself&lt;/span&gt;
    _marker&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PhantomData&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Indicating that we own a `T`&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Low level type related to allocation&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;RawVec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    ptr&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Unique&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    cap&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;usize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    buf&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RawVec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    len&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;usize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s mostly self-explanatory; a &lt;code&gt;Vec&amp;lt;T&amp;gt;&lt;/code&gt; is a pointer to &lt;code&gt;T&lt;/code&gt; with a set capacity and length. What about &lt;code&gt;abi_stable&lt;/code&gt;’s implementation?&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[repr(C)]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Notice this, so that it&#39;s FFI-safe&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#[derive(StableAbi)]&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// This trait marks `RVec` as FFI-safe, with info about its layout&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;RVec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; buffer&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;super&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; length&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;usize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    capacity&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;usize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    vtable&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;VecVTable_Ref&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    _marker&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PhantomData&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yup, basically the same, but packed inside a single struct. The single difference is that we have a field with the vtable. The conversion between these types is written with a macro, but if expanded, it looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;From&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RVec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;this&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;RVec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; this &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;std&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;mem&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ManuallyDrop&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;this&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;RVec&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            vtable&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;VTableGetter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;LIB_VTABLE&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            buffer&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; this&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;as_mut_ptr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            length&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; this&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            capacity&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; this&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;capacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
            _marker&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PhantomData&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The only “weird” part is the usage of &lt;code&gt;std::mem::ManuallyDrop&lt;/code&gt;, which is simply a wrapper that indicates Rust to not call the destructor of its contents automatically. In this case it’s basically a less error-prone &lt;code&gt;std::mem::forget&lt;/code&gt;, as &lt;a href=&quot;https://doc.rust-lang.org/stable/std/mem/fn.forget.html#relationship-with-manuallydrop&quot;&gt;its docs explain&lt;/a&gt;. Thanks to it, the memory from the &lt;code&gt;Vec&lt;/code&gt; won’t be dropped when this function ends, and its pointer ownership can be safely moved into &lt;code&gt;RVec&lt;/code&gt;, with no copying.&lt;/p&gt;
&lt;p&gt;This happens for every type I checked in &lt;code&gt;abi_stable&lt;/code&gt;, including &lt;code&gt;RSlice&amp;lt;T&amp;gt;&lt;/code&gt;, which contains a reference to a slice, &lt;code&gt;RStr&lt;/code&gt;, which is just a &lt;code&gt;RSlice&amp;lt;u8&amp;gt;&lt;/code&gt;, and &lt;code&gt;RString&lt;/code&gt;, which is just a &lt;code&gt;RVec&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_thread_safety&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;thread-safety&quot; tabindex=&quot;-1&quot;&gt;Thread safety &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#thread-safety&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;abi_stable&lt;/code&gt; uses &lt;code&gt;libloading&lt;/code&gt;, whose error-handling is not fully thread-safe on some platforms, such as &lt;code&gt;dlerror&lt;/code&gt; on FreeBSD&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#fn5&quot; id=&quot;fnref5&quot;&gt;[5]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#fn6&quot; id=&quot;fnref6&quot;&gt;[6]&lt;/a&gt;&lt;/sup&gt;. It’s fully thread-safe on Linux&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#fn7&quot; id=&quot;fnref7&quot;&gt;[7]&lt;/a&gt;&lt;/sup&gt;, macOS&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#fn8&quot; id=&quot;fnref8&quot;&gt;[8]&lt;/a&gt;&lt;/sup&gt;, and Windows&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#fn9&quot; id=&quot;fnref9&quot;&gt;[9]&lt;/a&gt;&lt;/sup&gt;, so for Tremor specifically we don’t have to worry about this. But if your programs supports other Operating Systems, you might want to check their manuals one by one in order to make sure.&lt;/p&gt;
&lt;p&gt;However, for the first version of our system this won’t be a problem at all. For simplicity’s sake, loading plugins after the startup will not be implemented yet, and we’ll do it sequentially. But it’s good to know it for the future.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_performance&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;performance&quot; tabindex=&quot;-1&quot;&gt;Performance &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#performance&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I first tried to write these benchmarks with &lt;a href=&quot;https://doc.rust-lang.org/nightly/cargo/commands/cargo-bench.html?highlight=feature&quot;&gt;cargo nightly’s implementation&lt;/a&gt;. However, since it’s so basic, not updated regularly, and requires nightly, I moved to &lt;a href=&quot;https://crates.io/crates/criterion&quot;&gt;&lt;code&gt;criterion&lt;/code&gt;&lt;/a&gt;, which I quite liked after using it for &lt;a href=&quot;https://nullderef.com/blog/web-api-client/&quot;&gt;another post&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;First, we can take a look at already implemented plugin systems in order to have an idea of the performance hit we’ll experience in Tremor. This is what we should expect once our system is polished and ready for deployment:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;nginx reports 20% slower startup times and up to a 5% slowdown in their execution times&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#fn10&quot; id=&quot;fnref10&quot;&gt;[10]&lt;/a&gt;&lt;/sup&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://www.technovelty.org/linux/plt-and-got-the-key-to-code-sharing-and-dynamic-libraries.html&quot;&gt;This article&lt;/a&gt; explains that the only performance difference is saving the &lt;a href=&quot;https://en.wikipedia.org/wiki/Position-independent_code&quot;&gt;resolved address&lt;/a&gt; of the symbol in a table the first time, and then it’s just a couple more instructions to access it. Also, obviously, the fact that the compiler can’t optimize parts of the code (e.g., inline function calls).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;These are the results of the benchmarks I wrote, on my not-so-fast laptop:&lt;/p&gt;
&lt;pre class=&quot;language-plain&quot;&gt;&lt;code class=&quot;language-plain&quot;&gt;dynamic setup           time:   [652.53 ns 654.72 ns 657.34 ns]
Found 7 outliers among 100 measurements (7.00%)
  3 (3.00%) high mild
  4 (4.00%) high severe

abi_stable setup        time:   [30.386 ns 30.477 ns 30.575 ns]
Found 9 outliers among 100 measurements (9.00%)
  7 (7.00%) high mild
  2 (2.00%) high severe

dynamic runtime         time:   [1.8814 ns 1.8878 ns 1.8947 ns]
Found 5 outliers among 100 measurements (5.00%)
  1 (1.00%) low mild
  2 (2.00%) high mild
  2 (2.00%) high severe

abi_stable runtime      time:   [3.2155 ns 3.2325 ns 3.2494 ns]
Found 3 outliers among 100 measurements (3.00%)
  1 (1.00%) low mild
  2 (2.00%) high mild

native runtime          time:   [817.39 ps 819.33 ps 821.38 ps]
Found 6 outliers among 100 measurements (6.00%)
  3 (3.00%) high mild
  3 (3.00%) high severe&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that the benchmarks still don’t represent a real usage of Tremor; it’s just using the plugin I described in this post with the &lt;code&gt;min&lt;/code&gt; function. But we can more or less analyze the performance differences between &lt;code&gt;abi_stable&lt;/code&gt; and raw dynamic loading — I doubt it’s worth implementing the final version with both methods just to run some benchmarks.&lt;/p&gt;
&lt;p&gt;The loading times aren’t so important for performance because they only happen once at the beginning of the program. But &lt;code&gt;abi_stable&lt;/code&gt;’s way of recursively checking the types in the plugins is not free; the difference with raw dynamic loading should be quite noticeable. But somehow, in my benchmarks &lt;code&gt;abi_stable&lt;/code&gt; was &lt;em&gt;way&lt;/em&gt; faster. What??&lt;/p&gt;
&lt;p&gt;It turns out that &lt;code&gt;abi_stable&lt;/code&gt; just leaks the library when it’s loaded to prevent a user-after-free. And since it won’t be unloaded anyway, it’s not much of a problem in terms of leaking memory. The library will be saved into a static variable (of type &lt;a href=&quot;https://docs.rs/abi_stable/latest/abi_stable/sabi_types/struct.LateStaticRef.html&quot;&gt;&lt;code&gt;LateStaticRef&lt;/code&gt;&lt;/a&gt;), and the next times it’s loaded the initial value will be reused. So in my bencharks for &lt;code&gt;abi_stable&lt;/code&gt;, loading only actually happens once, and for dynamic loading it happens for every iteration.&lt;/p&gt;
&lt;p&gt;Once the library is loaded, it seems that using dynamic loading versus static linking is quite bad, being more than twice as slow. This is understandable; the problem with the native benchmark was, and most likely still is, that the Rust compiler is too smart. If I called &lt;code&gt;min&lt;/code&gt; with fixed parameters — say &lt;code&gt;10.min(3)&lt;/code&gt; — it was optimized away, so I had to write a more intricate example that was different for each loop. Furthermore, using tools like &lt;code&gt;sabi_trait&lt;/code&gt; instead of a &lt;code&gt;void*&lt;/code&gt; almost doubles the execution time again.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_conclusion&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-abi-stable/#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;We’ve learned a lot about &lt;code&gt;abi_stable&lt;/code&gt; and the overall state of dynamic loading in Rust. We’ll definitely avoid a lot of work thanks to these dependencies. It’s not as bad as I thought; there’s plenty of tools for each use-case, though most are admittedly only in early stages.&lt;/p&gt;
&lt;p&gt;Hopefully, the performance degradations we’ve found won’t be as noticeable in the final version of the system. We’ll use &lt;code&gt;sabi_trait&lt;/code&gt; only when loading the library instead of for each call. And having a more complex use-case will probably avoid such incredible optimizations in the native code. You can find the full statistical reports in the &lt;a href=&quot;https://github.com/marioortizmanero/pdk-experiments/tree/master/criterion-reports&quot;&gt;&lt;code&gt;criterion-reports&lt;/code&gt;&lt;/a&gt; directory of the &lt;a href=&quot;https://github.com/marioortizmanero/pdk-experiments/&quot;&gt;repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;In the next article, I’ll cover the different caveats I’m finding as I try to actually implement the plugin system on Tremor, and the different ways in which they can be approached.&lt;/p&gt;

&lt;script src=&quot;https://f.convertkit.com/ckjs/ck.5.js&quot;&gt;&lt;/script&gt;
&lt;form action=&quot;https://app.kit.com/forms/7534343/subscriptions&quot; class=&quot;seva-form formkit-form&quot; method=&quot;post&quot; data-sv-form=&quot;7534343&quot; data-uid=&quot;3680381778&quot; data-format=&quot;inline&quot; data-version=&quot;5&quot; data-options=&#39;{&quot;settings&quot;:{&quot;after_subscribe&quot;:{&quot;action&quot;:&quot;message&quot;,&quot;success_message&quot;:&quot;Success! Check your email to confirm your subscription. If you can’t find it, look in the spam folder.&quot;,&quot;redirect_url&quot;:&quot;&quot;},&quot;analytics&quot;:{&quot;google&quot;:null,&quot;fathom&quot;:null,&quot;facebook&quot;:null,&quot;segment&quot;:null,&quot;pinterest&quot;:null,&quot;sparkloop&quot;:null,&quot;googletagmanager&quot;:null},&quot;modal&quot;:{&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;powered_by&quot;:{&quot;show&quot;:true,&quot;url&quot;:&quot;https://kit.com/features/forms?utm_campaign=poweredby&amp;amp;utm_content=form&amp;amp;utm_medium=referral&amp;amp;utm_source=dynamic&quot;},&quot;recaptcha&quot;:{&quot;enabled&quot;:false},&quot;return_visitor&quot;:{&quot;action&quot;:&quot;show&quot;,&quot;custom_content&quot;:&quot;&quot;},&quot;slide_in&quot;:{&quot;display_in&quot;:&quot;bottom_right&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;sticky_bar&quot;:{&quot;display_in&quot;:&quot;top&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15}},&quot;version&quot;:&quot;5&quot;}&#39; min-width=&quot;400 500 600 700 800&quot;&gt;
  &lt;div data-style=&quot;clean&quot;&gt;
    &lt;div class=&quot;formkit-header&quot; data-element=&quot;header&quot;&gt;
      &lt;h2&gt;Want to receive an email when I post?&lt;/h2&gt;
    &lt;/div&gt;
    &lt;ul class=&quot;formkit-alert formkit-alert-error&quot; data-element=&quot;errors&quot; data-group=&quot;alert&quot;&gt;&lt;/ul&gt;
    &lt;div data-element=&quot;fields&quot; data-stacked=&quot;false&quot; class=&quot;seva-fields formkit-fields&quot;&gt;
      &lt;div class=&quot;formkit-field&quot;&gt;
        &lt;input class=&quot;formkit-input&quot; name=&quot;email_address&quot; aria-label=&quot;Email Address&quot; placeholder=&quot;Email Address&quot; required=&quot;&quot; type=&quot;email&quot;&gt;
      &lt;/div&gt;
      &lt;button data-element=&quot;submit&quot; class=&quot;formkit-submit&quot; style=&quot;background-color:#e06c75;font-weight:700&quot;&gt;
        &lt;div class=&quot;formkit-spinner&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span class=&quot;&quot;&gt;Subscribe&lt;/span&gt;
      &lt;/button&gt;
    &lt;/div&gt;
    &lt;div class=&quot;formkit-guarantee&quot; data-element=&quot;guarantee&quot;&gt;
      &lt;p&gt;&lt;em&gt;Free and without spam. You can also use my &lt;a href=&quot;https://nullderef.com/index.xml&quot; target=&quot;_blank&quot;&gt;RSS&lt;/a&gt;, &lt;a href=&quot;https://x.com/null_deref&quot; target=&quot;_blank&quot;&gt;X&lt;/a&gt;, or &lt;a href=&quot;https://mstdn.social/@nullderef&quot; target=&quot;_blank&quot;&gt;Mastodon&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
      &lt;p style=&quot;opacity:.6;font-size:16px&quot;&gt;&lt;em&gt;Powered by &lt;a href=&quot;https://kit.com/&quot; target=&quot;_black&quot;&gt;Kit&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;style&gt;.formkit-form[data-uid=&quot;3680381778&quot;] a{text-decoration:underline}.formkit-form[data-uid=&quot;3680381778&quot;] legend{border:none;font-size:inherit;margin-bottom:10px;padding:0;position:relative;display:table}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-checkboxes,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-select{width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{border:0;border-radius:var(--radius);color:#fff;cursor:pointer;display:inline-block;text-align:center;font-weight:500;cursor:pointer;margin-bottom:15px;overflow:hidden;padding:0;position:relative;vertical-align:middle}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover{outline:0}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover&gt;span{background-color:rgba(0,0,0,.1)}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit&gt;span{display:block;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out;padding:12px 24px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input{background:#fff;padding:12px;border:1px solid var(--border);border-radius:var(--radius);-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;line-height:1.4;margin:0;-webkit-transition:border-color ease-out .3s;transition:border-color ease-out .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:focus{outline:0;border-color:#1677be;-webkit-transition:border-color ease .3s;transition:border-color ease .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-webkit-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-moz-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:-ms-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert{background:#f9fafb;border:1px solid #e3e3e3;border-radius:5px;-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;list-style:none;margin:25px auto;padding:12px;text-align:center;width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert:empty{display:none}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-success{background:#d3fbeb;border-color:#10bf7a;color:#0c905c}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-error{background:#fde8e2;border-color:#f2643b;color:#ea4110}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;height:0;width:0;margin:0 auto;position:absolute;top:0;left:0;right:0;width:0;overflow:hidden;text-align:center;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div{margin:auto;width:12px;height:12px;background-color:#fff;opacity:.3;border-radius:100%;display:inline-block;-webkit-animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both;animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:first-child{-webkit-animation-delay:-.32s;animation-delay:-.32s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:nth-child(2){-webkit-animation-delay:-.16s;animation-delay:-.16s}.formkit-form[data-uid=&quot;3680381778&quot;]{border:1px solid var(--border);border-radius:var(--radius);position:relative;overflow:hidden;background:var(--code-bg)}.formkit-form[data-uid=&quot;3680381778&quot;]{padding:20px;width:100%;position:relative}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-header{margin:0 0 27px 0;font-size:20px;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee{margin-top:10px;margin-bottom:32px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee&gt;p{margin:0;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-fields{display:flex;flex-wrap:wrap;margin:25px auto 0 auto;gap:10px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-field,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{flex:1 0 100%}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;]{padding:0 30px}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-field{flex:100 1 auto}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-submit{flex:1 1 auto}&lt;/style&gt;
&lt;/form&gt;
&lt;h2 id=&quot;footnotes&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/h33p/cglue/issues/3&quot;&gt;A few questions about the library &lt;span class=&quot;gh-number&quot;&gt;h33p/cglue#3&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://doc.rust-lang.org/nomicon/ffi.html#ffi-and-panics&quot;&gt;FFI and panics — Rustonomicon&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://doc.rust-lang.org/book/ch09-01-unrecoverable-errors-with-panic.html&quot;&gt;Unrecoverable Errors with &lt;code&gt;panic!&lt;/code&gt; — The Rust Programming Language&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn4&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://doc.rust-lang.org/nomicon/unwinding.html&quot;&gt;Unwinding — Rustonomicon&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#fnref4&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn5&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://docs.rs/libloading/0.7.1/libloading/struct.Library.html#thread-safety&quot;&gt;Thread-safety — Libloading v0.7.1&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#fnref5&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn6&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://pubs.opengroup.org/onlinepubs/009604499/functions/dlerror.html&quot;&gt;&lt;code&gt;dlerror&lt;/code&gt; — The Open Group Base Specifications&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#fnref6&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn7&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://man7.org/linux/man-pages/man3/dlerror.3.html#ATTRIBUTES&quot;&gt;&lt;code&gt;dlerror&lt;/code&gt; attributes — Linux Manual Page&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#fnref7&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn8&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man3/dlerror.3.html&quot;&gt;&lt;code&gt;dlerror&lt;/code&gt; — Mac OS X Man Pages&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#fnref8&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn9&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://docs.microsoft.com/en-us/windows/win32/api/errhandlingapi/nf-errhandlingapi-setthreaderrormode&quot;&gt;&lt;code&gt;SetThreadErrorMode&lt;/code&gt; — Microsoft Documentation&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#fnref9&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn10&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://httpd.apache.org/docs/2.4/dso.html#advantages&quot;&gt;Advantages and Disadvantages — Dynamic Shared Object (DSO) Support&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-abi-stable/#fnref10&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</content>
  </entry>
  <entry>
    <title>Designing an API Client in Rust: New RSpotify Version a Year Later</title>
    <link href="https://nullderef.com/blog/web-api-client/" />
    <updated>2021-10-13T00:00:00Z</updated>
    <id>https://nullderef.com/blog/web-api-client/</id>
    <content type="html">&lt;details class=&quot;toc&quot;&gt;
&lt;summary&gt;Table of Contents&lt;/summary&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#the-story&quot;&gt;The story &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#making-the-api-http-client-agnostic&quot;&gt;Making the API HTTP-client agnostic &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#finding-a-more-robust-architecture&quot;&gt;Finding a more robust architecture &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#configuration&quot;&gt;Configuration &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#runtime-over-compile-time&quot;&gt;Runtime over compile-time &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#sane-defaults&quot;&gt;Sane defaults &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#flexibility&quot;&gt;Flexibility &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#taking-borrowed-generic-parameters&quot;&gt;Taking borrowed/generic parameters &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#optional-parameters&quot;&gt;Optional parameters &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#splitting-up-into-multiple-crates&quot;&gt;Splitting up into multiple crates &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#documentation&quot;&gt;Documentation &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#introducing-how-to-use-the-crate&quot;&gt;Introducing how to use the crate &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#helping-users-upgrade&quot;&gt;Helping users upgrade &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#macros&quot;&gt;Macros &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#other-goodies&quot;&gt;Other goodies &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#cached-and-self-refreshing-tokens&quot;&gt;Cached and self-refreshing tokens &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#type-safe-wrappers-for-id-types&quot;&gt;Type-safe wrappers for ID types &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#automatic-pagination&quot;&gt;Automatic pagination &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#simplify-wrapper-model-objects&quot;&gt;Simplify wrapper model objects &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#measuring-the-changes&quot;&gt;Measuring the changes &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#statistics&quot;&gt;Statistics &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#execution-time&quot;&gt;Execution time &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#special-thanks&quot;&gt;Special thanks &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/details&gt;
&lt;p&gt;This article in &lt;a href=&quot;https://nullderef.com/series/rspotify&quot;&gt;the Rspotify series&lt;/a&gt; describes my journey of basically rewriting the entirety of &lt;a href=&quot;https://github.com/ramsayleung/rspotify&quot;&gt;this Rust library&lt;/a&gt;; around 13 months of work (in my free time), starting at September 2020, up until October 2021. I think this has given me enough experience for an article regarding API design on Rust, and perhaps those who attempt the same in the future can take some ideas and save time. Specially considering how much I’ve gone in circles and how much time I’ve “wasted” implementing things that ended up being discarded.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_the_story&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-story&quot; tabindex=&quot;-1&quot;&gt;The story &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/web-api-client/#the-story&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;If you don’t care about RSpotify’s specific story, you can jump to the “&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#actual_start&quot;&gt;Making the API HTTP-client agnostic&lt;/a&gt;” section, but I think knowing a bit the motives for this rewrite is interesting for other open source maintainers in the same situation as me. This all started when I wanted to use a Rust Spotify API wrapper for one of my projects at that time, &lt;a href=&quot;https://vidify.org/&quot;&gt;Vidify&lt;/a&gt;. I went to &lt;a href=&quot;https://crates.io/&quot;&gt;crates.io&lt;/a&gt; and looked for “Spotify”, and all I found was &lt;a href=&quot;https://crates.io/crates/rspotify&quot;&gt;&lt;code&gt;rspotify&lt;/code&gt;&lt;/a&gt; (the most popular one), &lt;a href=&quot;https://crates.io/crates/aspotify&quot;&gt;&lt;code&gt;aspotify&lt;/code&gt;&lt;/a&gt; (an asynchronous-only alternative), and a bunch of other outdated or incomplete libraries.&lt;/p&gt;
&lt;p&gt;The author of &lt;code&gt;aspotify&lt;/code&gt; had made a brand-new library from scratch, because as he commented in &lt;a href=&quot;https://www.reddit.com/r/rust/comments/ehz66s/aspotify_an_asynchronous_rust_spotify_web_api/&quot;&gt;its release&lt;/a&gt;, “[rspotify’s] API in general I found hard to use and confusing”. Honestly, depending on the state of the original library this is the easiest choice because you don’t have to worry about backwards compatibility or old code.&lt;/p&gt;
&lt;p&gt;A year back I had also helped with the first steps of &lt;a href=&quot;https://github.com/felix-hilden/tekore&quot;&gt;&lt;code&gt;tekore&lt;/code&gt;&lt;/a&gt;, an alternative to &lt;a href=&quot;https://github.com/plamere/spotipy&quot;&gt;&lt;code&gt;spotipy&lt;/code&gt;&lt;/a&gt;, the most popular Spotify library for Python out there. The problem was that back then, Paul, spotipy’s creator and maintainer, &lt;a href=&quot;https://github.com/plamere/spotipy/issues/387&quot;&gt;was absent&lt;/a&gt;, and the library really needed a push to upgrade from Python 2 and improve the overall interface and user-friendliness, which had become pretty poor.&lt;/p&gt;
&lt;p&gt;A few devs, including &lt;a href=&quot;https://github.com/felix-hilden&quot;&gt;Felix&lt;/a&gt;, tried to start from scratch. Felix was the most dedicated, and Paul eventually granted him permissions to upload new spotipy releases, meaning that he could rewrite the entire thing under the same name, and upload it to PyPi (Python’s official third-party repositories) if he wanted to. The problem was that spotipy had a good amount of users by that point. A couple of them ended up stopping Felix from completely breaking backward compatibility, so he made it into a brand-new library instead and left the maintainance tasks to the new devs in charge. Tekore still is, in my very biased opinion and at the time of this writing, a much nicer to use library than spotipy. But it has taken a lot of time to get a user base starting from scratch, and it’s still not as popular as spotipy by far.&lt;/p&gt;
&lt;p&gt;Having gone through this, I also wanted to see if it would be possible to give RSpotify a face wash without anyone complaining. RSpotify was still in beta – version 0.10 – so you can’t expect to have a stable API, but that’s far from a full rewrite. At the very least I wanted to talk with Ramsay, the original RSpotify developer, and see what his thoughts were. I proposed some big changes like &lt;a href=&quot;https://github.com/ramsayleung/rspotify/issues/112&quot;&gt;rewriting the &lt;code&gt;blocking&lt;/code&gt; module&lt;/a&gt; and &lt;a href=&quot;https://github.com/Kestrer&quot;&gt;Kestrer&lt;/a&gt; wrote up &lt;a href=&quot;https://github.com/ramsayleung/rspotify/issues/127&quot;&gt;an issue with ~80 issues he had found when using the library&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I think it’s fair to release the new version 0.11 as such, rather than as a new library, for the following reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Ramsay, the author, also wanted to help.&lt;/li&gt;
&lt;li&gt;The fundamentals of the library would remain the same: being the most user-friendly feature-full library for the Spotify API.&lt;/li&gt;
&lt;li&gt;It was considered a necessary evil. In my opinion, the library was doomed without a big rewrite. Otherwise, it’d eventually become really hard to maintain and work with. I admit that said breaking changes could’ve been smaller, but I thought that, while I’m at it, I might as well make it as close as possible to a perfect Rust interface.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Also, note that I am in no way associated with Spotify (though open to offers :P). I’m just a Computer Science student whose sole goal was to use Rust for a non-trivial case, from start to finish. I was somewhat experienced with the Spotify API, so I decided RSpotify was a good choice.&lt;/p&gt;
&lt;p&gt;Now that most of the stuff I talk about in this article is finished, you can read about the changes in detail &lt;a href=&quot;https://github.com/ramsayleung/rspotify/blob/master/CHANGELOG.md&quot;&gt;in the CHANGELOG&lt;/a&gt;. The following sections will talk about what I learned about API Design, and some tips for those writing one in Rust.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;actual_start&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;making-the-api-http-client-agnostic&quot; tabindex=&quot;-1&quot;&gt;Making the API HTTP-client agnostic &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/web-api-client/#making-the-api-http-client-agnostic&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;RSpotify is now HTTP-client agnostic, which means that it can work with whichever HTTP library the user configures without adding much overhead. For now, we support &lt;a href=&quot;https://crates.io/crates/ureq&quot;&gt;&lt;code&gt;ureq&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://crates.io/crates/reqwest&quot;&gt;&lt;code&gt;reqwest&lt;/code&gt;&lt;/a&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;. The non-trivial part about this is that the HTTP client can be either blocking (&lt;code&gt;ureq&lt;/code&gt;) or asynchronous (&lt;code&gt;reqwest&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;While it’s fully implemented in the new version, it’s the only part that isn’t finished yet and that’s subject to change. This has to do with the way we support both async and sync HTTP clients under the same trait. In order to have a common interface for both kinds of programming, we use the &lt;a href=&quot;https://crates.io/crates/maybe_async&quot;&gt;&lt;code&gt;maybe_async&lt;/code&gt;&lt;/a&gt; crate, which lets us switch between them with a feature.&lt;/p&gt;
&lt;p&gt;The trait used as a base for any HTTP client is implemented with async. That way, if the &lt;code&gt;maybe_async/is_sync&lt;/code&gt; feature is disabled in the &lt;code&gt;Cargo.toml&lt;/code&gt;, the trait and its implementations remain the same, and if it’s enabled, all the occurrences of &lt;code&gt;async&lt;/code&gt; and &lt;code&gt;.await&lt;/code&gt; are removed. It’s also possible to add special cases where removing &lt;code&gt;async&lt;/code&gt;/&lt;code&gt;.await&lt;/code&gt; isn’t enough. This works perfectly, but unfortunately breaks a rule in Cargo’s feature system: features must be strictly additive, and &lt;code&gt;maybe_async&lt;/code&gt;’s feature is a &lt;em&gt;toggle&lt;/em&gt; – you can’t have both a sync and an async client at the same time.&lt;/p&gt;
&lt;p&gt;Since we don’t follow that rule, those who depend on RSpotify with both async and sync (directly or indirectly) won’t be able to compile their project. I won’t get into much detail because I’m writing an entire separate post dedicated for this issue that I’ll publish once this whole ordeal is fixed. We’re keeping track of it in &lt;a href=&quot;https://github.com/ramsayleung/rspotify/issues/221&quot;&gt;#221&lt;/a&gt; for now, for those who want to learn more.&lt;/p&gt;
&lt;p&gt;For now, I’ll just explain how it works for clients of the same type: either synchronous or asynchronous. Since the usage for an HTTP client in an API is often very basic, i.e., you only need to make requests and get the results, we can cover its usage under a single interface. For example:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;trait&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;HttpClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Send&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Default&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
   &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        url&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        headers&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        payload&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;post&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        url&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        headers&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        payload&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// etc&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, we can add an implementation for each of our supported clients:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;UreqClient&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpClient&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;UreqClient&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Error&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;ureq&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Transport&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token attribute attr-name&quot;&gt;#[inline]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        url&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        headers&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Headers&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        payload&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; request &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;ureq&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;url&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; sender &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token closure-params&quot;&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Request&lt;/span&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; val&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;in&lt;/span&gt; payload&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;iter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
                req &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;key&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; val&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            req&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;request&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; headers&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; sender&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Internal more complex handler&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// etc&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we can finally make our client generic over its internal HTTP client:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Spotify&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpClient&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    http&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// etc&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;HttpClient&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Spotify&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Http&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; headers &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token macro property&quot;&gt;todo!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; payload &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token macro property&quot;&gt;todo!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/some/endpoint&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; headers&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; payload&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Beware that this introduces a good amount of additional complexity which is probably unnecessary for your own API wrapper. But this was definitely something interesting for RSpotify: some crates that already depend on us like &lt;a href=&quot;https://github.com/hrkfdn/ncspot&quot;&gt;&lt;code&gt;ncspot&lt;/code&gt;&lt;/a&gt; or &lt;a href=&quot;https://github.com/Spotifyd/spotifyd&quot;&gt;&lt;code&gt;spotifyd&lt;/code&gt;&lt;/a&gt; are blocking, and others like &lt;a href=&quot;https://github.com/Rigellute/spotify-tui&quot;&gt;&lt;code&gt;spotify-tui&lt;/code&gt;&lt;/a&gt; use async. I thought I might as well try, and I’ve finally figured out how to make it work, even for both async and sync.&lt;/p&gt;
&lt;p&gt;We implement all of this in the crate &lt;a href=&quot;https://github.com/ramsayleung/rspotify/tree/master/rspotify-http&quot;&gt;&lt;code&gt;rspotify-http&lt;/code&gt;&lt;/a&gt;, which I plan on &lt;a href=&quot;https://github.com/ramsayleung/rspotify/issues/234&quot;&gt;moving into a separate crate&lt;/a&gt; for the whole community to use once it’s working as I want it to. I think this is a pretty neat feature for an API client that will hopefully become easier to implement in the future (and first of all work properly).&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_finding_a_more_robust_architecture&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;finding-a-more-robust-architecture&quot; tabindex=&quot;-1&quot;&gt;Finding a more robust architecture &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/web-api-client/#finding-a-more-robust-architecture&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Another key refactor I worked on for RSpotify was its architecture. The Spotify API in particular has &lt;a href=&quot;https://developer.spotify.com/documentation/general/guides/authorization-guide/&quot;&gt;multiple authorization methods&lt;/a&gt; that give you access to a different set of endpoints. For example, if you’re using &lt;em&gt;client credentials&lt;/em&gt; (the most basic one), then you can’t access an endpoint to modify the user’s data; you need &lt;a href=&quot;https://en.wikipedia.org/wiki/OAuth&quot;&gt;OAuth information&lt;/a&gt;. This used to work with the &lt;a href=&quot;https://doc.rust-lang.org/1.0.0/style/ownership/builders.html&quot;&gt;&lt;em&gt;builder pattern&lt;/em&gt;&lt;/a&gt;, following this structure (though not exactly the same):&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// OAuth information&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; oauth &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SpotifyOAuth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;redirect_uri&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;http://localhost:8888/callback&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;scope&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;user-modify-playback-state&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Basic information&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; creds &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SpotifyClientCredentials&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;client_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;this-is-my-client-id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;client_secret&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;this-is-my-client-secret&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Obtaining the access token&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; token &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;get_token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; oauth&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// The client itself&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; spotify &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Spotify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;client_credentials_manager&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;creds&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;token_info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;token&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Performing a request&lt;/span&gt;
spotify&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;seek_track&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;25000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I wanted something more tailored towards our specific application. I think the builder pattern is great, but it might become too verbose or confusing:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Do we really need it for &lt;code&gt;Credentials&lt;/code&gt;, which always takes the same two parameters?&lt;/li&gt;
&lt;li&gt;Which authorization method are we using above again? Currently, it’s possible to call &lt;code&gt;seek_track&lt;/code&gt; after having followed an authorization process that doesn’t give access to it. And since we’re mixing all of them under the same client it quickly becomes a mess, having many &lt;code&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt; fields that are only &lt;code&gt;Some&lt;/code&gt; for specific authorization methods. So, what if we have a Spotify client for each authorization method?&lt;/li&gt;
&lt;li&gt;Wouldn’t it be nice to have some type safety, too? The &lt;code&gt;unwrap&lt;/code&gt; hurts my eyes.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;After removing the builder pattern and being more explicit about the authorization method that’s being used, this is more or less what we get:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// OAuth information&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; oauth &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OAuth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;http://localhost:8888/callback&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;user-read-currently-playing&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// Basic information&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; creds &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Credentials&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;my-client-id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;my-client-secret&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// The client itself, now clearly with the &quot;authorization code&quot; method&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; spotify &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthCodeSpotify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;creds&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; oauth&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Obtaining the access token&lt;/span&gt;
spotify&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;prompt_for_token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Performing a request&lt;/span&gt;
spotify&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;seek_track&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;25000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And if the user wants something more advanced, they can always write this:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; oauth &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;OAuth&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    redirect_uri&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;http://localhost:8888/callback&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    state&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;generate_random_string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;alphabets&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;ALPHANUM&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    scopes&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;user-read-currently-playing&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It’s sufficient to use the regular initialization pattern for this case because we don’t even need validation. If we did, we could always just add a few setters or checks before its usage and we’re done. Ask yourself: do you really need the builder pattern? In this case we certainly didn’t.&lt;/p&gt;
&lt;p&gt;The most complicated part of the refactor is having a client for each authorization method, and making sure the user can only call those endpoints they have access to. There are &lt;em&gt;many&lt;/em&gt; ways to approach this, I just had to decide which one was the best. I gave this a lot of thought&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Having multiple clients seems trivial with inheritance, with a base from which they can extend. In Rust, we could follow the typical “composition over inheritance” principle:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;EndpointsBase&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    http&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Rc&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Http&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// Shared with the rest of the endpoints&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EndpointsBase&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endpoint1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/endpoint1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endpoint2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/endpoint2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// etc&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;EndpointsOAuth&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    token&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    http&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Rc&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Http&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EndpointsOAuth&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endpoint3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_oauth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/endpoint3&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;token&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endpoint4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;http&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_oauth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/endpoint4&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;token&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// etc&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;AuthCodeSpotify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;EndpointsBase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EndpointsOAuth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthCodeSpotify&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;authenticate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* ... */&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;EndpointsBase&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;oauth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;EndpointsOAuth&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The user can then write &lt;code&gt;spotify.base().endpoint1()&lt;/code&gt; or &lt;code&gt;spotify.oauth().endpoint3()&lt;/code&gt; to access the endpoints in their different groups. However, all of them have to share a single HTTP client and other information such as the config or the token, so we have to use something like &lt;code&gt;Rc&lt;/code&gt;. We can improve this by taking ideas from &lt;a href=&quot;https://crates.io/crates/aspotify&quot;&gt;&lt;code&gt;aspotify&lt;/code&gt;&lt;/a&gt;, another popular crate for the Spotify API, which groups up the endpoints by categories. Their endpoint groups take a reference to the client itself instead, which is pretty neat and works just as well. Here is a simplified version of &lt;a href=&quot;https://play.rust-lang.org/?version=stable&amp;amp;mode=debug&amp;amp;edition=2018&amp;amp;gist=6cce195451518fcf644e7506ca7b51b2&quot;&gt;this Rust Playground&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;trait&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Spotify&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;get_http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;get_token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;EndpointsBase&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Spotify&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Spotify&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EndpointsBase&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endpoint1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/endpoint1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endpoint2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/endpoint2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// etc&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;EndpointsOAuth&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Spotify&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Spotify&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EndpointsOAuth&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;S&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endpoint3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_oauth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/endpoint3&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endpoint4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_oauth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/endpoint4&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// etc&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;AuthCodeSpotify&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; http&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; token&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Token&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Spotify&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthCodeSpotify&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;get_http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Http&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;http &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;get_token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Token&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;token &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthCodeSpotify&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;authenticate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* ... */&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EndpointsBase&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EndpointsBase&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;oauth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EndpointsOAuth&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;_&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;Self&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EndpointsOAuth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;However, you might think using just &lt;code&gt;spotify.endpoint1()&lt;/code&gt; instead of &lt;code&gt;spotify.base().endpoint1()&lt;/code&gt; is more suitable for your particular API client. The only way to do that would be to delegate every single endpoint manually into the main client. Some people use &lt;code&gt;Deref&lt;/code&gt; and &lt;code&gt;DerefMut&lt;/code&gt; in order to automatically do it, but that’s a common anti-pattern&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#fn4&quot; id=&quot;fnref4&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;I tried different approaches, and my favorite ended up being a trait-based interface. All you need is a couple traits with the endpoint implementations, which require a getter to the HTTP client or similars. Here is a simplified version of &lt;a href=&quot;https://play.rust-lang.org/?version=stable&amp;amp;mode=debug&amp;amp;edition=2018&amp;amp;gist=901e41d16172e17368328c5a7744f673&quot;&gt;this Rust Playground&lt;/a&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;trait&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;EndpointsBase&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;get_http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endpoint1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/endpoint1&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endpoint2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/endpoint2&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// etc&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;trait&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;EndpointsOAuth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EndpointsBase&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;get_token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endpoint3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_oauth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/endpoint3&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endpoint4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_oauth&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;/endpoint4&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// etc&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;AuthCodeSpotify&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthCodeSpotify&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;authenticate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;/* ... */&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EndpointsBase&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthCodeSpotify&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;get_http&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Http&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;EndpointsOAuth&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;for&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AuthCodeSpotify&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;get_token&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Token&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This way, as long as the user has these traits in scope, they can access the endpoints with just &lt;code&gt;spotify.endpoint1()&lt;/code&gt;. We can make that easier by including a &lt;a href=&quot;https://stackoverflow.com/questions/36384840/what-is-the-prelude&quot;&gt;prelude&lt;/a&gt; in the library with these traits, so that all the user has to do is &lt;code&gt;use rspotify::prelude::*&lt;/code&gt;. Another big advantage this provides is that it’s extremely flexible. The user can declare their own client and implement its internal functionality themselves, while still having access to the endpoints, which is the boring task that probably doesn’t need customization. And even if they wanted to, they could just override the trait implementation.&lt;/p&gt;
&lt;p&gt;The main issue with the trait-based solution is that you can’t use &lt;code&gt;-&amp;gt; impl Trait&lt;/code&gt; in trait methods as of Rust 1.55&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#fn5&quot; id=&quot;fnref5&quot;&gt;[5]&lt;/a&gt;&lt;/sup&gt;. We unfortunately need these, specially with asynchronous clients, because async trait methods are &lt;code&gt;-&amp;gt; impl Future&lt;/code&gt; after all. For now, we can work around it by erasing the types with the &lt;a href=&quot;https://crates.io/crates/async-trait&quot;&gt;&lt;code&gt;async-trait&lt;/code&gt;&lt;/a&gt; crate. Supposedly, this will be temporary until GATs are implemented, which isn’t too far off&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#fn6&quot; id=&quot;fnref6&quot;&gt;[6]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;Both of these solutions also make it hard to have private functions in the base client, because the shared parts are in a trait. We don’t really want the user to have access to the methods &lt;code&gt;get&lt;/code&gt; or &lt;code&gt;get_oauth&lt;/code&gt;. It’s defined in the client/trait because it’s useful for every client, but for the end user it’s just noise in the documentation. This isn’t that much of a big deal because you can just declare the item with &lt;code&gt;#[doc(hidden)]&lt;/code&gt; so that it doesn’t appear in the documentation.&lt;/p&gt;
&lt;p&gt;So yeah, there are no &lt;em&gt;perfect&lt;/em&gt; solutions, but these are two of the best ones I could find. The choice is up to the designer of the library and their needs. Having multiple clients let us &lt;a href=&quot;https://github.com/ramsayleung/rspotify/issues/150&quot;&gt;implement PKCE Authentication for RSpotify quite easily&lt;/a&gt;, so it’s worth it in the end anyway. Our final architecture looks like this (&lt;em&gt;diagram by Ramsay&lt;/em&gt;):&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/img/78BI3_gihi-809.avif 809w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/img/78BI3_gihi-809.webp 809w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://nullderef.com/img/78BI3_gihi-809.png&quot; alt=&quot;diagram&quot; width=&quot;809&quot; height=&quot;629&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_configuration&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;configuration&quot; tabindex=&quot;-1&quot;&gt;Configuration &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/web-api-client/#configuration&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a name=&quot;_runtime_over_compile_time&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;runtime-over-compile-time&quot; tabindex=&quot;-1&quot;&gt;Runtime over compile-time &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/web-api-client/#runtime-over-compile-time&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There are a few parts of the Spotify client that can be customized by the user. Previously, these were just fields of the main client, but since we now have multiple clients, it might be worth moving into a separate struct to avoid duplication.&lt;/p&gt;
&lt;p&gt;Anyhow, one of our fails was attempting to use features instead of the &lt;code&gt;Config&lt;/code&gt; struct for configuration, on the assumption that features would be more performant. Which one is faster?&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cached_token &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Saving cache token to the file!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token attribute attr-name&quot;&gt;#[cfg(feature = &lt;span class=&quot;token string&quot;&gt;&quot;cached_token&quot;&lt;/span&gt;)]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Saving cache token to the file!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Turns out that both of these are usually compiled to the same machine code anyway. Since &lt;code&gt;self.config.cached_token&lt;/code&gt; is most times specified as a constant, optimizing it away is one of the more basic tasks a compiler can do. Features are drastically less flexible and harder to use than runtime variables, so before introducing one you should really think about it. Apart from the fact that you obviously can’t use features at runtime (which is a possible use-case here), they are applied globally, so you can’t have two different clients, one with cached tokens and another without them. In order to take this decision I actually wrote an entire article about it, so &lt;a href=&quot;https://nullderef.com/blog/rust-features/&quot;&gt;check it out if you want more details&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Even though it’s basic, I keep forgetting about this: don’t get obsessed with performance. As you add new features to the crate, it’s completely natural that some overheads are introduced here and there. And even then, they might not even be noticeable. First of all get that new feature working. Then, measure the real effect on performance. And finally, if it’s more than you expected, then actually think about optimizing it.&lt;/p&gt;
&lt;p&gt;One correct usage would be our new &lt;code&gt;cli&lt;/code&gt; feature. We have some utilities for command-line programs, such as prompting for the user’s credentials. However, not everyone needs these, such as servers, and it introduced the &lt;a href=&quot;https://crates.io/crates/webbrowser&quot;&gt;&lt;code&gt;webbrowser&lt;/code&gt;&lt;/a&gt; dependency and a few unnecessary functions. So we decided to move this into a separate feature for those interested, which is disabled by default.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_sane_defaults&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;sane-defaults&quot; tabindex=&quot;-1&quot;&gt;Sane defaults &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/web-api-client/#sane-defaults&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;On the topic of configuration, it’s important to have sane defaults as well. This is highly subjective, but I prefer to do as little as possible under the hood &lt;em&gt;without the user knowing about it&lt;/em&gt;. When initializing a client we used to automatically try to read from the environment variables. If that didn’t work then we tried to use the default values or we just panicked in the builder:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; creds &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SpotifyClientCredentials&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// this reads the env variables&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;client_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;this-is-my-client-id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;client_secret&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;this-is-my-client-secret&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a pretty useful feature, but we can’t be sure the writer/reader of the code knows about it, and it could potentially cause unintended behaviour. Instead, we can just have a &lt;code&gt;default&lt;/code&gt; method that does nothing special, which is what the user would expect, and also &lt;code&gt;from_env&lt;/code&gt;, which &lt;em&gt;explicitly&lt;/em&gt; tells us what it does:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; creds &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;SpotifyClientCredentials&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from_env&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// this reads the env variables&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;client_id&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;this-is-my-client-id&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;client_secret&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;this-is-my-client-secret&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a name=&quot;_flexibility&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;flexibility&quot; tabindex=&quot;-1&quot;&gt;Flexibility &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/web-api-client/#flexibility&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a name=&quot;_taking_borrowedgeneric_parameters&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;taking-borrowed-generic-parameters&quot; tabindex=&quot;-1&quot;&gt;Taking borrowed/generic parameters &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/web-api-client/#taking-borrowed-generic-parameters&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Friendly reminder: generally, it’s better to take a &lt;code&gt;&amp;amp;str&lt;/code&gt; than a &lt;code&gt;String&lt;/code&gt; in a function&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#fn7&quot; id=&quot;fnref7&quot;&gt;[7]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#fn8&quot; id=&quot;fnref8&quot;&gt;[8]&lt;/a&gt;&lt;/sup&gt;. The same thing applies to the owned type &lt;code&gt;Vec&amp;lt;T&amp;gt;&lt;/code&gt;; it’s probably a better idea to take a &lt;code&gt;&amp;amp;[T]&lt;/code&gt; instead, or the even more fancy &lt;code&gt;impl IntoIterator&amp;lt;Item = T&amp;gt;&lt;/code&gt;. The last option makes it possible to pass iterators to the function without requiring a &lt;code&gt;collect&lt;/code&gt;, which not only is more user-friendly, but also avoids a memory allocation. Its only downside is that the function signatures become a bit uglier, and all the consequences of using generics. Either of these options are fine, really, so it’s up to you.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_optional_parameters&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;optional-parameters&quot; tabindex=&quot;-1&quot;&gt;Optional parameters &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/web-api-client/#optional-parameters&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Similarly, if the functions in your library frequently include optional parameters (i.e., of type &lt;code&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt;), you might want to consider other ways to handle them. In our case, we were using generics with &lt;code&gt;Into&amp;lt;Option&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt; in order to not have to wrap the parameters in &lt;code&gt;Some&lt;/code&gt; when passing them to the function, but it wasn’t consistent. We finally agreed that using plain &lt;code&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt; was good enough because it simplifies the function definition in the docs and it’s less magic&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#fn9&quot; id=&quot;fnref9&quot;&gt;[9]&lt;/a&gt;&lt;/sup&gt;. But the important part is that we made it &lt;em&gt;consistent&lt;/em&gt;; the decision itself between &lt;code&gt;Into&amp;lt;Option&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt; or &lt;code&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt; wasn’t that important. After doing research about this topic, I wrote up an article with more details &lt;a href=&quot;https://nullderef.com/blog/rust-parameters/&quot;&gt;here&lt;/a&gt;, in case you want to learn more.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_splitting_up_into_multiple_crates&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;splitting-up-into-multiple-crates&quot; tabindex=&quot;-1&quot;&gt;Splitting up into multiple crates &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/web-api-client/#splitting-up-into-multiple-crates&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Another cool idea that promotes flexibility is separating the wrapper into multiple crates. In RSpotify, we now have a total of four of them (credits to Ramsay for the diagram):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;rspotify-http&lt;/code&gt;: the multi-HTTP client abstraction, which I plan on making more generic and moving into a separate crate for everyone to use&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#fn10&quot; id=&quot;fnref10&quot;&gt;[10]&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rspotify-macros&lt;/code&gt;: a small crate with macros&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rspotify-model&lt;/code&gt;: the full model for the RSpotify crate&lt;/li&gt;
&lt;li&gt;&lt;code&gt;rspotify&lt;/code&gt;: the implementation of the clients&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/img/UI3WHhY_Kk-530.avif 530w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/img/UI3WHhY_Kk-530.webp 530w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://nullderef.com/img/UI3WHhY_Kk-530.png&quot; alt=&quot;diagram&quot; width=&quot;530&quot; height=&quot;246&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;The most important one here is splitting up the wrapper into the model and the clients. The model is generic enough that it can be used by any client, even outside RSpotify. Some users have to implement their own custom clients for different reasons, and pulling our model helps to avoid lots of complexity and maintainance work&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#fn11&quot; id=&quot;fnref11&quot;&gt;[11]&lt;/a&gt;&lt;/sup&gt;. It can also be shared with other public crates, such as &lt;code&gt;aspotify&lt;/code&gt;, and join forces in keeping the model up to date&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#fn12&quot; id=&quot;fnref12&quot;&gt;[12]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_documentation&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;documentation&quot; tabindex=&quot;-1&quot;&gt;Documentation &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/web-api-client/#documentation&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a name=&quot;_introducing_how_to_use_the_crate&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;introducing-how-to-use-the-crate&quot; tabindex=&quot;-1&quot;&gt;Introducing how to use the crate &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/web-api-client/#introducing-how-to-use-the-crate&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This might be obvious to some, but it isn’t enough to document every single public item in your library. You also have to introduce the user how to work with it in the top-level documentation. Some ideas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;List the goals, current and future features of the crate, and things you don’t plan working on. Perhaps also add a comparison with similar crates; these are usually super helpful.&lt;/li&gt;
&lt;li&gt;Write a small getting started guide, explaining the most important items in the crate and what they do.&lt;/li&gt;
&lt;li&gt;Add some notes about the architecture of your crate. This is specially useul to those who want to contribute. For RSpotify, Ramsay created the diagrams included in this article, and added more details in the &lt;a href=&quot;https://github.com/ramsayleung/rspotify/blob/master/README.md&quot;&gt;README&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Explain the Cargo features in your crate and how to use them.&lt;/li&gt;
&lt;li&gt;Make sure you have a few examples working. It’s the easiest way to get started, in my opinion.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a name=&quot;_helping_users_upgrade&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;helping-users-upgrade&quot; tabindex=&quot;-1&quot;&gt;Helping users upgrade &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/web-api-client/#helping-users-upgrade&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Since this change was going to break so much code, I wanted to make sure that the upgrade is as less painful as possible. This can be achieved in many ways:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Make sure you prove why these breaking changes are actually necessary. It will feel like less of a waste of time to the user.&lt;/li&gt;
&lt;li&gt;Include a &lt;a href=&quot;https://github.com/ramsayleung/rspotify/blob/master/CHANGELOG.md&quot;&gt;changelog&lt;/a&gt;, either as an indepent file, or in the release notes. In RSpotify, we make habit of adding a new line to the changelog for every release that includes a new feature or breaking changes. To be honest, in our case it’s turned out quite messy because we had &lt;em&gt;so many changes&lt;/em&gt;, but in a regular update it should be nicer to read.&lt;/li&gt;
&lt;li&gt;It might be a good idea to &lt;a href=&quot;https://github.com/ramsayleung/rspotify/issues/218&quot;&gt;create an issue in your repository&lt;/a&gt; where you provide help directly to those who try to upgrade and
have problems with it.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a name=&quot;_macros&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;macros&quot; tabindex=&quot;-1&quot;&gt;Macros &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/web-api-client/#macros&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Macros in Rust are pretty cool! But you don’t want to overdo them either. In &lt;code&gt;rspotify&lt;/code&gt; we frequently had to build hashmaps or JSON objects; at least once per endpoint. Some of the parameters in the endpoints were mandatory, and others optional (passed as an &lt;code&gt;Option&lt;/code&gt;):&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; params &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Query&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with_capacity&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;ids&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; ids&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;limit&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; limit&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to_string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;ref&lt;/span&gt; market&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; market &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    params&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;insert&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;market&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; market&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;as_ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I first tried to simplify this by using macros to their full strength, so my initial attempts looked like this:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; params &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token macro property&quot;&gt;build_map!&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    ids&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    limit &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; limit&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to_string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    optional market &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; market&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;as_ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Or this:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; params &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token macro property&quot;&gt;build_map!&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    ids&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    limit &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; limit&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to_string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;market&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; market&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;as_ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Yes, they are &lt;em&gt;very&lt;/em&gt; concise and we remove a lot of boilerplate, but they’re bad for two reasons:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;There’s too much magic going on:
&lt;ul&gt;
&lt;li&gt;They turn the &lt;code&gt;ids&lt;/code&gt;/&lt;code&gt;limit&lt;/code&gt;/&lt;code&gt;market&lt;/code&gt; identifiers into a string with &lt;a href=&quot;https://doc.rust-lang.org/std/macro.stringify.html&quot;&gt;&lt;code&gt;stringify!&lt;/code&gt;&lt;/a&gt; and use that as the key for the hashmap insertion.&lt;/li&gt;
&lt;li&gt;In the expression to the right of an optional parameter, its value isn’t treated as an &lt;code&gt;Option&lt;/code&gt; anymore; there’s a hidden &lt;code&gt;if let Some(market)&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The syntax is weird. In order to understand them correctly, you’d probably have to look up their documentation and read it first.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The &lt;a href=&quot;https://github.com/ramsayleung/rspotify/issues/202&quot;&gt;final design&lt;/a&gt; still reduces the boilerplate needed in each endpoint considerably, but there’s no magic going on. It’s basically the same as a regular hashmap builder macro like you’d find on &lt;a href=&quot;https://docs.rs/maplit/1.0.2/maplit/&quot;&gt;&lt;code&gt;maplit&lt;/code&gt;&lt;/a&gt;, and the macro doesn’t hide anything:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; params &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token macro property&quot;&gt;build_map!&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;ids&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; ids&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;limit&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; limit&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to_string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    optional &lt;span class=&quot;token string&quot;&gt;&quot;market&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; market&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token closure-params&quot;&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;x&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;as_ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Anyhow, we might remove it in the future, since this syntax will soon work as well&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/web-api-client/#fn13&quot; id=&quot;fnref13&quot;&gt;[13]&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;HashMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;k1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; v1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;k2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; v2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a name=&quot;_other_goodies&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;other-goodies&quot; tabindex=&quot;-1&quot;&gt;Other goodies &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/web-api-client/#other-goodies&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Some new features we added to RSpotify that might be of interest specifically for other web API wrappers:&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_cached_and_self_refreshing_tokens&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;cached-and-self-refreshing-tokens&quot; tabindex=&quot;-1&quot;&gt;Cached and self-refreshing tokens &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/web-api-client/#cached-and-self-refreshing-tokens&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Cached tokens are automatically saved into a file, encoded for example in JSON, and then attempted to be loaded again when restarting the application.&lt;/p&gt;
&lt;p&gt;Before making a request, self-refreshing tokens check if they are expired, and in that case perform the re-authorization process automatically.&lt;/p&gt;
&lt;div style=&quot;text-align:center&quot;&gt;
    &lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&amp;nbsp;&lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/ramsayleung/rspotify/issues/223&quot;&gt;Implement cache token and refresh token &lt;span class=&quot;gh-number&quot;&gt;ramsayleung/rspotify#223&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a name=&quot;_type_safe_wrappers_for_id_types&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;type-safe-wrappers-for-id-types&quot; tabindex=&quot;-1&quot;&gt;Type-safe wrappers for ID types &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/web-api-client/#type-safe-wrappers-for-id-types&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In the Spotify API, items such as artists or tracks are identified by a unique ID string. The URI is the ID, but prefixed by its type, for example &lt;code&gt;spotify:track:4cOdK2wGLETKBW3PvgPWqT&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Many endpoints previously took the URI parameters as a String. That meant we had to manually check that their type were what we were expecting, and also that they were valid (they’re usually made up of alphanumeric characters).&lt;/p&gt;
&lt;p&gt;Instead, we now have an &lt;code&gt;Id&lt;/code&gt; trait and structs that implement it, like &lt;code&gt;ArtistId&lt;/code&gt; or &lt;code&gt;TrackId&lt;/code&gt;, keeping its type known at compile time and also at runtime with &lt;code&gt;dyn Id&lt;/code&gt;. If you take a &lt;code&gt;TrackId&lt;/code&gt; as a parameter, then you already know its type, and that its contents are valid, so you’re ready to use it.&lt;/p&gt;
&lt;div style=&quot;text-align:center&quot;&gt;
    &lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&amp;nbsp;&lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/ramsayleung/rspotify/pull/161&quot;&gt;Initial id type proposal &lt;span class=&quot;gh-number&quot;&gt;ramsayleung/rspotify#161&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;
    &lt;br&gt;
    &lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&amp;nbsp;&lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/ramsayleung/rspotify/pull/244&quot;&gt;Fix IDs v4 &lt;span class=&quot;gh-number&quot;&gt;ramsayleung/rspotify#244&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a name=&quot;_automatic_pagination&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;automatic-pagination&quot; tabindex=&quot;-1&quot;&gt;Automatic pagination &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/web-api-client/#automatic-pagination&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Many API servers have paginated replies for large lists. Instead of sending a huge object, it splits it up into multiple packets, and sends them one by one along with an index to the position in the list. Then, the user can stop requesting them at any time and potentially only end up using a portion of that originally huge object.&lt;/p&gt;
&lt;p&gt;In Rust, this can be abstracted away very naturally with &lt;a href=&quot;https://doc.rust-lang.org/std/iter/trait.Iterator.html&quot;&gt;iterators&lt;/a&gt; in sync programs, and &lt;a href=&quot;https://rust-lang.github.io/async-book/05_streams/01_chapter.html&quot;&gt;streams&lt;/a&gt; for async. The latter can be implemented easily in your crate thanks to &lt;a href=&quot;https://crates.io/crates/async_stream&quot;&gt;&lt;code&gt;async_stream&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;div style=&quot;text-align:center&quot;&gt;
    &lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&amp;nbsp;&lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/ramsayleung/rspotify/issues/124&quot;&gt;Add unlimited endpoints &lt;span class=&quot;gh-number&quot;&gt;ramsayleung/rspotify#124&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a name=&quot;_simplify_wrapper_model_objects&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;simplify-wrapper-model-objects&quot; tabindex=&quot;-1&quot;&gt;Simplify wrapper model objects &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/web-api-client/#simplify-wrapper-model-objects&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Due to how JSON works, sometimes an object will always have a single field:&lt;/p&gt;
&lt;pre class=&quot;language-javascript&quot;&gt;&lt;code class=&quot;language-javascript&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string-property property&quot;&gt;&quot;many_artists&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token comment&quot;&gt;// ..&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// ...&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In that case, instead of just deserializing that object with &lt;a href=&quot;https://crates.io/crates/serde&quot;&gt;&lt;code&gt;serde&lt;/code&gt;&lt;/a&gt; and returning it to the user, you can just return that one field in the object:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[derive(Deserialize)]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;ArtistCollection&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    many_artists&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Artists&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Before&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ArtistCollection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;serde_json&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from_str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// After&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Vec&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Artists&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; response &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token namespace&quot;&gt;serde_json&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from_str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ArtistCollection&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;response&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token closure-params&quot;&gt;&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;x&lt;span class=&quot;token closure-punctuation punctuation&quot;&gt;|&lt;/span&gt;&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;many_artists&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div style=&quot;text-align:center&quot;&gt;
    &lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt;&amp;nbsp;&lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/ramsayleung/rspotify/issues/149&quot;&gt;The way to reduce wrapper object &lt;span class=&quot;gh-number&quot;&gt;ramsayleung/rspotify#149&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;
&lt;/div&gt;
&lt;p&gt;&lt;a name=&quot;_measuring_the_changes&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;measuring-the-changes&quot; tabindex=&quot;-1&quot;&gt;Measuring the changes &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/web-api-client/#measuring-the-changes&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Since this release changed so much stuff and took so long, I wanted to get a detailed comparison between v0.10 and v0.11 for different aspects of the library – not just performance.&lt;/p&gt;
&lt;p&gt;The full source for these benchmarks is available at the &lt;a href=&quot;https://github.com/marioortizmanero/rspotify-bench&quot;&gt;marioortizmanero/rspotify-bench&lt;/a&gt; repository. Note that I had to apply a small patch to the v0.10 version because by now it didn’t work correctly.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_statistics&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;statistics&quot; tabindex=&quot;-1&quot;&gt;Statistics &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/web-api-client/#statistics&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Some parts of RSpotify can be analyzed statically, such as the lines of code
that will need to be maintained, or its number of dependencies. Here’s an
example as of October 12, 2021:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Version&lt;/th&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Rust LoC&lt;/th&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Dependencies in tree&lt;/th&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Dependencies in tree (all features)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;0.10.0&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;11281&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;132&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;141&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td style=&quot;text-align:center&quot;&gt;master&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;7525&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;101&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;123&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;The Lines of Code in the old version were quite bloated because of the &lt;code&gt;blocking&lt;/code&gt; module, which was a copy-paste of the async client. Still, these were lines that needed to be maintained, so they count just as much. On the other hand, we now have a much more extensive set of tests and new features that add up. In total, we have about 33% less lines to be maintained.&lt;/p&gt;
&lt;p&gt;The number of dependencies has decreased both by default and with all the features enabled. &lt;a href=&quot;https://github.com/ramsayleung/rspotify/issues/108&quot;&gt;We cleaned up a lot of them&lt;/a&gt; and tried to keep the defaults leaner. Since the new version adds more features such as PKCE, we even had to add new dependencies like &lt;a href=&quot;https://crates.io/crates/sha2&quot;&gt;&lt;code&gt;sha2&lt;/code&gt;&lt;/a&gt;, but it’s still a clear win.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_execution_time&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;execution-time&quot; tabindex=&quot;-1&quot;&gt;Execution time &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/web-api-client/#execution-time&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The execution benchmarks use &lt;a href=&quot;https://github.com/bheisler/criterion.rs&quot;&gt;Criterion&lt;/a&gt;, with a total of 100 iterations on my Dell Vostro 5481 laptop, or more specifically, Intel i5-8265U (8) @ 3.900GHz. The full reports are available in the &lt;code&gt;report&lt;/code&gt; directory of each benchmark.&lt;/p&gt;
&lt;p&gt;Taking a look at the Criterion reports, it seems that the Spotify API doesn’t intentionally slow down responses when it’s being “spammed”, so it should be fine in that regard:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/img/H1hJDnuc67-450.avif 450w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/img/H1hJDnuc67-450.webp 450w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://nullderef.com/img/H1hJDnuc67-450.png&quot; alt=&quot;&quot; width=&quot;450&quot; height=&quot;300&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Note that comparing the blocking clients for now is unfair, because instead of using &lt;code&gt;reqwest::blocking&lt;/code&gt;, now it’s &lt;code&gt;ureq&lt;/code&gt;. Furthermore, the async and sync versions can’t be compared either, since the former requires setting up the tokio runtime and a bunch of other stuff.&lt;/p&gt;
&lt;p&gt;The asynchronous clients in both versions should give a rough idea of the actual differences, though you can tell it’s just a quick benchmark, and the results shouldn’t be taken too seriously in the first place. Here’s an example as of October 12, 2021:&lt;/p&gt;
&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Version&lt;/th&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Debug Compilation Time (s)&lt;/th&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Release Compilation Time (s)&lt;/th&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Benchmarking Time (ms/iter)&lt;/th&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Release Binary Size (MB)&lt;/th&gt;
&lt;th style=&quot;text-align:center&quot;&gt;Release Stripped Binary Size (MB)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;0.10, blocking&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;72.7&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;126.2&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;271.3&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;9.9&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;4.9&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.10, async&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;72.2&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;115.7&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;428.0&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;11&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;5.2&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.11, blocking (ureq)&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;38.5&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;55.9&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;503.6&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;7.3&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;2.6&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;0.11, async (reqwest)&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;51.014&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;86.594&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;432.49&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;8.5&lt;/td&gt;
&lt;td style=&quot;text-align:center&quot;&gt;4.0&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;I also wanted to reflect the compilation time, since it’s a possibility that we have less dependencies, but of larger size. The results show that this isn’t the case, since it takes 29% less time to build in debug mode, and 25% less time in release mode.&lt;/p&gt;
&lt;p&gt;In terms of execution time, I didn’t expect it to be any better. Even though the architecture and implementation is cleaner, some of the new features introduce noticeable overhead. For example, now that we have automatically refreshing tokens, the &lt;code&gt;Token&lt;/code&gt; has to be saved in an &lt;code&gt;Arc&amp;lt;Mutex&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt;, which means we’re locking and unlocking at least once per request. Still, the difference is negligible: just a 1% increase.&lt;/p&gt;
&lt;p&gt;The cleanup and all these dependencies we removed mean that the resulting binary is also smaller, and by a lot: there’s a 23% decrease in its size.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_special_thanks&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;special-thanks&quot; tabindex=&quot;-1&quot;&gt;Special thanks &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/web-api-client/#special-thanks&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This release has been possible thanks to &lt;a href=&quot;https://github.com/ramsayleung&quot;&gt;@ramsayleung&lt;/a&gt;, &lt;a href=&quot;https://github.com/kstep&quot;&gt;@kstep&lt;/a&gt;, &lt;a href=&quot;https://github.com/hellbound22&quot;&gt;@hellbound22&lt;/a&gt;, &lt;a href=&quot;https://github.com/Qluxzz&quot;&gt;@Qluxzz&lt;/a&gt;, &lt;a href=&quot;https://github.com/icewind1991&quot;&gt;@icewind1991&lt;/a&gt;, &lt;a href=&quot;https://github.com/aramperes&quot;&gt;@aramperes&lt;/a&gt;, &lt;a href=&quot;https://github.com/Sydpy&quot;&gt;@Sydpy&lt;/a&gt;, &lt;a href=&quot;https://github.com/arlyon&quot;&gt;@arlyon&lt;/a&gt;, &lt;a href=&quot;https://github.com/flip1995&quot;&gt;@flip1995&lt;/a&gt;, and &lt;a href=&quot;https://github.com/Rigellute&quot;&gt;@Rigellute&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;I’m specially grateful towards Ramsay, who apart from contributing many of the features I listed here, read and reviewed &lt;em&gt;every single one of my issues and pull requests&lt;/em&gt;. I’ve learned how important it is to have a second opinon, and someone else who proofreads everything before you merge dumb stuff into &lt;code&gt;master&lt;/code&gt;. Note that I &lt;em&gt;did&lt;/em&gt; proofread my own ideas and pull requests, but there are some things that you just don’t notice on time, as much as you try to. This is a problem that I think is particularly relevant in open source. I personally had worked on projects alone most of the time, and the difference is huge. I would suggest everyone to try to join forces with at least one more person when working in side projects.&lt;/p&gt;
&lt;p&gt;That’s all! I hope this post provided insightful information and that you learned something from it. Remember that you can leave a comment at the bottom in case you want to discuss it.&lt;/p&gt;
&lt;p&gt;Lots of love, &lt;br&gt;
Mario&lt;/p&gt;

&lt;script src=&quot;https://f.convertkit.com/ckjs/ck.5.js&quot;&gt;&lt;/script&gt;
&lt;form action=&quot;https://app.kit.com/forms/7534343/subscriptions&quot; class=&quot;seva-form formkit-form&quot; method=&quot;post&quot; data-sv-form=&quot;7534343&quot; data-uid=&quot;3680381778&quot; data-format=&quot;inline&quot; data-version=&quot;5&quot; data-options=&#39;{&quot;settings&quot;:{&quot;after_subscribe&quot;:{&quot;action&quot;:&quot;message&quot;,&quot;success_message&quot;:&quot;Success! Check your email to confirm your subscription. If you can’t find it, look in the spam folder.&quot;,&quot;redirect_url&quot;:&quot;&quot;},&quot;analytics&quot;:{&quot;google&quot;:null,&quot;fathom&quot;:null,&quot;facebook&quot;:null,&quot;segment&quot;:null,&quot;pinterest&quot;:null,&quot;sparkloop&quot;:null,&quot;googletagmanager&quot;:null},&quot;modal&quot;:{&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;powered_by&quot;:{&quot;show&quot;:true,&quot;url&quot;:&quot;https://kit.com/features/forms?utm_campaign=poweredby&amp;amp;utm_content=form&amp;amp;utm_medium=referral&amp;amp;utm_source=dynamic&quot;},&quot;recaptcha&quot;:{&quot;enabled&quot;:false},&quot;return_visitor&quot;:{&quot;action&quot;:&quot;show&quot;,&quot;custom_content&quot;:&quot;&quot;},&quot;slide_in&quot;:{&quot;display_in&quot;:&quot;bottom_right&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;sticky_bar&quot;:{&quot;display_in&quot;:&quot;top&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15}},&quot;version&quot;:&quot;5&quot;}&#39; min-width=&quot;400 500 600 700 800&quot;&gt;
  &lt;div data-style=&quot;clean&quot;&gt;
    &lt;div class=&quot;formkit-header&quot; data-element=&quot;header&quot;&gt;
      &lt;h2&gt;Want to receive an email when I post?&lt;/h2&gt;
    &lt;/div&gt;
    &lt;ul class=&quot;formkit-alert formkit-alert-error&quot; data-element=&quot;errors&quot; data-group=&quot;alert&quot;&gt;&lt;/ul&gt;
    &lt;div data-element=&quot;fields&quot; data-stacked=&quot;false&quot; class=&quot;seva-fields formkit-fields&quot;&gt;
      &lt;div class=&quot;formkit-field&quot;&gt;
        &lt;input class=&quot;formkit-input&quot; name=&quot;email_address&quot; aria-label=&quot;Email Address&quot; placeholder=&quot;Email Address&quot; required=&quot;&quot; type=&quot;email&quot;&gt;
      &lt;/div&gt;
      &lt;button data-element=&quot;submit&quot; class=&quot;formkit-submit&quot; style=&quot;background-color:#e06c75;font-weight:700&quot;&gt;
        &lt;div class=&quot;formkit-spinner&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span class=&quot;&quot;&gt;Subscribe&lt;/span&gt;
      &lt;/button&gt;
    &lt;/div&gt;
    &lt;div class=&quot;formkit-guarantee&quot; data-element=&quot;guarantee&quot;&gt;
      &lt;p&gt;&lt;em&gt;Free and without spam. You can also use my &lt;a href=&quot;https://nullderef.com/index.xml&quot; target=&quot;_blank&quot;&gt;RSS&lt;/a&gt;, &lt;a href=&quot;https://x.com/null_deref&quot; target=&quot;_blank&quot;&gt;X&lt;/a&gt;, or &lt;a href=&quot;https://mstdn.social/@nullderef&quot; target=&quot;_blank&quot;&gt;Mastodon&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
      &lt;p style=&quot;opacity:.6;font-size:16px&quot;&gt;&lt;em&gt;Powered by &lt;a href=&quot;https://kit.com/&quot; target=&quot;_black&quot;&gt;Kit&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;style&gt;.formkit-form[data-uid=&quot;3680381778&quot;] a{text-decoration:underline}.formkit-form[data-uid=&quot;3680381778&quot;] legend{border:none;font-size:inherit;margin-bottom:10px;padding:0;position:relative;display:table}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-checkboxes,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-select{width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{border:0;border-radius:var(--radius);color:#fff;cursor:pointer;display:inline-block;text-align:center;font-weight:500;cursor:pointer;margin-bottom:15px;overflow:hidden;padding:0;position:relative;vertical-align:middle}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover{outline:0}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover&gt;span{background-color:rgba(0,0,0,.1)}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit&gt;span{display:block;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out;padding:12px 24px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input{background:#fff;padding:12px;border:1px solid var(--border);border-radius:var(--radius);-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;line-height:1.4;margin:0;-webkit-transition:border-color ease-out .3s;transition:border-color ease-out .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:focus{outline:0;border-color:#1677be;-webkit-transition:border-color ease .3s;transition:border-color ease .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-webkit-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-moz-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:-ms-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert{background:#f9fafb;border:1px solid #e3e3e3;border-radius:5px;-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;list-style:none;margin:25px auto;padding:12px;text-align:center;width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert:empty{display:none}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-success{background:#d3fbeb;border-color:#10bf7a;color:#0c905c}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-error{background:#fde8e2;border-color:#f2643b;color:#ea4110}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;height:0;width:0;margin:0 auto;position:absolute;top:0;left:0;right:0;width:0;overflow:hidden;text-align:center;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div{margin:auto;width:12px;height:12px;background-color:#fff;opacity:.3;border-radius:100%;display:inline-block;-webkit-animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both;animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:first-child{-webkit-animation-delay:-.32s;animation-delay:-.32s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:nth-child(2){-webkit-animation-delay:-.16s;animation-delay:-.16s}.formkit-form[data-uid=&quot;3680381778&quot;]{border:1px solid var(--border);border-radius:var(--radius);position:relative;overflow:hidden;background:var(--code-bg)}.formkit-form[data-uid=&quot;3680381778&quot;]{padding:20px;width:100%;position:relative}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-header{margin:0 0 27px 0;font-size:20px;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee{margin-top:10px;margin-bottom:32px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee&gt;p{margin:0;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-fields{display:flex;flex-wrap:wrap;margin:25px auto 0 auto;gap:10px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-field,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{flex:1 0 100%}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;]{padding:0 30px}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-field{flex:100 1 auto}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-submit{flex:1 1 auto}&lt;/style&gt;
&lt;/form&gt;
&lt;h2 id=&quot;footnotes&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/ramsayleung/rspotify/pull/129&quot;&gt;Multiple clients via features &lt;span class=&quot;gh-number&quot;&gt;ramsayleung/rspotify#129&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; &lt;a href=&quot;https://nullderef.com/blog/web-api-client/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/ramsayleung/rspotify/issues/173&quot;&gt;Restructure the authentication process &lt;span class=&quot;gh-number&quot;&gt;ramsayleung/rspotify#173&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; &lt;a href=&quot;https://nullderef.com/blog/web-api-client/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.reddit.com/r/rust/comments/lkdw6o/designing_a_new_architecture_for_rspotify_based/&quot;&gt;Designing a new architecture for RSpotify based on trait inheritance, need opinions - Reddit&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/web-api-client/#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn4&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/rust-unofficial/patterns/blob/main/anti_patterns/deref.md&quot;&gt;&lt;code&gt;Deref&lt;/code&gt; polymorphism&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/web-api-client/#fnref4&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn5&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://stackoverflow.com/questions/39482131/is-it-possible-to-use-impl-trait-as-a-functions-return-type-in-a-trait-defini&quot;&gt;Is it possible to use &lt;code&gt;impl Trait&lt;/code&gt; as a function’s return type in a trait definition? - StackOverFlow&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/web-api-client/#fnref5&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn6&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/rust-lang/rust/issues/4426&quot;&gt;Tracking issue for generic associated types (GAT) &lt;span class=&quot;gh-number&quot;&gt;rust-lang/rust#4426&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; &lt;a href=&quot;https://nullderef.com/blog/web-api-client/#fnref6&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn7&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://hermanradtke.com/2015/05/03/string-vs-str-in-rust-functions.html&quot;&gt;String vs &amp;amp;str in Rust functions - hermanradtke.com&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/web-api-client/#fnref7&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn8&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/ramsayleung/rspotify/pull/206&quot;&gt;Pass parameters by reference and use iterators wherever possible &lt;span class=&quot;gh-number&quot;&gt;ramsayleung/rspotify#206&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; &lt;a href=&quot;https://nullderef.com/blog/web-api-client/#fnref8&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn9&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/ramsayleung/rspotify/issues/134&quot;&gt;Optional parameters &lt;span class=&quot;gh-number&quot;&gt;ramsayleung/rspotify#134&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; &lt;a href=&quot;https://nullderef.com/blog/web-api-client/#fnref9&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn10&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/ramsayleung/rspotify/issues/234&quot;&gt;Use an external HTTP universal interface instead of &lt;code&gt;rspotify-http&lt;/code&gt; &lt;span class=&quot;gh-number&quot;&gt;ramsayleung/rspotify#234&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; &lt;a href=&quot;https://nullderef.com/blog/web-api-client/#fnref10&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn11&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M7.177 3.073L9.573.677A.25.25 0 0110 .854v4.792a.25.25 0 01-.427.177L7.177 3.427a.25.25 0 010-.354zM3.75 2.5a.75.75 0 100 1.5.75.75 0 000-1.5zm-2.25.75a2.25 2.25 0 113 2.122v5.256a2.251 2.251 0 11-1.5 0V5.372A2.25 2.25 0 011.5 3.25zM11 2.5h-1V4h1a1 1 0 011 1v5.628a2.251 2.251 0 101.5 0V5A2.5 2.5 0 0011 2.5zm1 10.25a.75.75 0 111.5 0 .75.75 0 01-1.5 0zM3.75 12a.75.75 0 100 1.5.75.75 0 000-1.5z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/ramsayleung/rspotify/pull/191&quot;&gt;Move model into a separate rspotify-model crate &lt;span class=&quot;gh-number&quot;&gt;ramsayleung/rspotify#191&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; &lt;a href=&quot;https://nullderef.com/blog/web-api-client/#fnref11&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn12&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/KaiJewson/aspotify/issues/14&quot;&gt;Sharing the model with rspotify-model &lt;span class=&quot;gh-number&quot;&gt;KaiJewson/aspotify#14&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; &lt;a href=&quot;https://nullderef.com/blog/web-api-client/#fnref12&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn13&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://twitter.com/mgattozzi/status/1447983152669020160?t=jAGevaOOh___cWGERcLLgQ&quot;&gt;New hashmap constructor - @gmattozzi, Twitter&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/web-api-client/#fnref13&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</content>
  </entry>
  <entry>
    <title>Plugins in Rust: Diving into Dynamic Loading</title>
    <link href="https://nullderef.com/blog/plugin-dynload/" />
    <updated>2021-10-05T00:00:00Z</updated>
    <id>https://nullderef.com/blog/plugin-dynload/</id>
    <content type="html">&lt;details class=&quot;toc&quot;&gt;
&lt;summary&gt;Table of Contents&lt;/summary&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#learning-more-about-connectors&quot;&gt;Learning more about connectors &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#about-tremor&quot;&gt;About Tremor &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#my-next-steps&quot;&gt;My next steps &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#on-software-engineering&quot;&gt;On software engineering &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#how-tremor-works&quot;&gt;How Tremor works &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#taking-a-look-at-ebpf-first&quot;&gt;Taking a look at eBPF first &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#getting-deeper-into-dynamic-linking&quot;&gt;Getting deeper into dynamic linking &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#versioning&quot;&gt;Versioning &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#loading-plugins&quot;&gt;Loading plugins &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#handling-state&quot;&gt;Handling state &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#generics-in-plugins&quot;&gt;Generics in plugins? &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#dyn-in-plugins&quot;&gt;dyn in plugins? &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#the-c-way&quot;&gt;The C way &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#error-handling&quot;&gt;Error Handling &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#missing-fields&quot;&gt;Missing fields &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#version-mismatch&quot;&gt;Version mismatch &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#wrong-type&quot;&gt;Wrong type &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#wrong-address&quot;&gt;Wrong address &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#panicking&quot;&gt;Panicking &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#full-implementation&quot;&gt;Full implementation &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#conclusion&quot;&gt;Conclusion &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/details&gt;
&lt;p&gt;In the &lt;a href=&quot;https://nullderef.com/blog/plugin-start/&quot;&gt;last article&lt;/a&gt; of this &lt;a href=&quot;https://nullderef.com/series/rust-plugins/&quot;&gt;series&lt;/a&gt; I wrote some simple experiments of plugins both with WebAssembly and dynamic loading. After discarding Wasm for this specific plugin system, I wanted to try to get a more realistic example with dynamic loading and work towards the final implementation.&lt;/p&gt;
&lt;p&gt;Since the Tremor team suggested that I began by implementing connectors, we’ll first have to learn more about them. Matthias ran me through their current state in a meeting, which I’ll try to summarize. We won’t need to know much for this specific article, though.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_learning_more_about_connectors&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;learning-more-about-connectors&quot; tabindex=&quot;-1&quot;&gt;Learning more about connectors &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-dynload/#learning-more-about-connectors&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;












&lt;p&gt;First of all, &lt;a href=&quot;https://www.tremor.rs/&quot;&gt;Tremor&lt;/a&gt; is an event processing system for unstructured data. One of its many usages may be to:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Receive logs from different applications in your business.&lt;/li&gt;
&lt;li&gt;Filter, transform, and mix them up following the same structure.&lt;/li&gt;
&lt;li&gt;Send all the now structured logs to your database.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/blog/plugin-dynload/Dq4N7J1CYp-741.avif 741w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/blog/plugin-dynload/Dq4N7J1CYp-741.webp 741w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://nullderef.com/blog/plugin-dynload/Dq4N7J1CYp-741.png&quot; alt=&quot;Processing example diagram&quot; width=&quot;741&quot; height=&quot;321&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;This currently works with &lt;a href=&quot;https://www.tremor.rs/docs/artefacts/onramps/&quot;&gt;onramps/sources&lt;/a&gt;, &lt;a href=&quot;https://www.tremor.rs/docs/artefacts/offramps/&quot;&gt;offramps/sinks&lt;/a&gt; and pipelines:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;An onramp specifies how Tremor connects to the outside world (or pipeline) in order to &lt;em&gt;receive&lt;/em&gt; from external systems, such as &lt;a href=&quot;https://www.tremor.rs/docs/artefacts/onramps/#tcp&quot;&gt;TCP&lt;/a&gt;, &lt;a href=&quot;https://www.tremor.rs/docs/artefacts/onramps/#metronome&quot;&gt;periodically&lt;/a&gt; or &lt;a href=&quot;https://www.tremor.rs/docs/artefacts/onramps/#postgresql&quot;&gt;PostgreSQL&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;An offramp specifies how Tremor connects to the outside world (or pipeline) in order to &lt;em&gt;publish&lt;/em&gt; to external systems, such as &lt;a href=&quot;https://www.tremor.rs/docs/artefacts/offramps/#stdout&quot;&gt;stdout&lt;/a&gt;, &lt;a href=&quot;https://www.tremor.rs/docs/artefacts/offramps/#kafka&quot;&gt;Kafka&lt;/a&gt; or &lt;a href=&quot;https://www.tremor.rs/docs/artefacts/offramps/#elastic&quot;&gt;ElasticSearch&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;A pipeline is a set of operations (transformation, aggregation, dropping, etc) through which events can be routed.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The thing is that some onramps may not only want to receive from external systems, but also respond to them directly, acting like an offramp, and vice-versa. This is currently implemented with what’s called &lt;a href=&quot;https://www.tremor.rs/docs/operations/linked-transports/&quot;&gt;“linked transports”&lt;/a&gt;, and it’s specifically useful for some onramps and offramps like REST and websocket, where the protocol already provides facility for responding to events with a single connection, for example with an ACK.&lt;/p&gt;
&lt;p&gt;Basically, &lt;a href=&quot;https://github.com/tremor-rs/tremor-rfcs/blob/connectors-n-streams/text/0000-connectors-streams.md&quot;&gt;connectors&lt;/a&gt; are just a way to abstract over both onramps and offramps under the same concept, including linked transports. As the time of writing this article they’re still being implemented by Matthias in the &lt;a href=&quot;https://github.com/tremor-rs/tremor-runtime/tree/connectors&quot;&gt;&lt;code&gt;connectors&lt;/code&gt; branch&lt;/a&gt; of &lt;a href=&quot;https://github.com/tremor-rs/tremor-runtime&quot;&gt;tremor-rs/tremor-runtime&lt;/a&gt;, but their interface, defined with the &lt;a href=&quot;https://github.com/tremor-rs/tremor-runtime/blob/883f13e29b4c6ec7b6703f2487aac321c738e7c8/src/connectors.rs#L739&quot;&gt;&lt;code&gt;Connector&lt;/code&gt; trait&lt;/a&gt;, is somewhat stable.&lt;/p&gt;
&lt;p&gt;It’s important to keep the plugin interface as simple as possible. The communication details should be left to the runtime, so that the plugin can be simplified to just exporting a number of synchronous functions. With this we can avoid passing some complex types (&lt;code&gt;async&lt;/code&gt;, channels, etc) between the runtime and plugin, which can be impossible if you have to maintain ABI stability (&lt;a href=&quot;https://crates.io/crates/abi_stable&quot;&gt;&lt;code&gt;abi_stable&lt;/code&gt;&lt;/a&gt; doesn’t even support &lt;code&gt;async&lt;/code&gt;).&lt;/p&gt;
&lt;p&gt;Once this lean plugin interface is defined, we can create some kind of wrapper in the runtime (a &lt;em&gt;manager&lt;/em&gt;, in Tremor terms) that handles communication and other similar tasks. This exact same thing is done by other crates such as &lt;a href=&quot;https://crates.io/crates/rdkafka&quot;&gt;&lt;code&gt;rdkafka&lt;/code&gt;&lt;/a&gt;, which is based on the C library &lt;a href=&quot;https://crates.io/crates/rdkafka-sys&quot;&gt;&lt;code&gt;rdkafka-sys&lt;/code&gt;&lt;/a&gt;, and implements a higher-level asynchronous interface on top of it.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_about_tremor&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;about-tremor&quot; tabindex=&quot;-1&quot;&gt;About Tremor &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-dynload/#about-tremor&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;As always, these articles include a first section with content specific to Tremor that you might &lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#actual_start&quot;&gt;want to skip&lt;/a&gt;. Unfortunately, with time this series will become more and more specific to Tremor; after all I’m just reporting my progress on their project. Still, having a step-by-step walkthrough for a real-life Plugin System will surely be helpful to those attempting to do the same.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_my_first_steps&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;my-next-steps&quot; tabindex=&quot;-1&quot;&gt;My next steps &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-dynload/#my-next-steps&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In the first meeting we discussed the work I had exposed in my last update. Despite the complications (being forced to use &lt;code&gt;#[repr(C)]&lt;/code&gt;), the team liked where the plugin system was going.&lt;/p&gt;
&lt;p&gt;They suggested me to start with connectors for the real-life example, even though they were incomplete because Matthias was still working on them. The best way to do this would be to copy the bare minimum from &lt;a href=&quot;https://github.com/tremor-rs/tremor-runtime&quot;&gt;Tremor’s repository&lt;/a&gt; and try to get the simplest Proof of Concept working.&lt;/p&gt;
&lt;p&gt;In previous meetings we had discussed the possibility of having generics in the interface, but that turned out to not be necessary at all. The &lt;code&gt;Connector&lt;/code&gt; trait had a workaround to avoid generics with &lt;code&gt;SinkManagerBuilder&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_on_software_engineering&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;on-software-engineering&quot; tabindex=&quot;-1&quot;&gt;On software engineering &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-dynload/#on-software-engineering&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;At the end of the first meeting, Darach gave some very interesting advice for my software engineering career, so I took note of it and reflected for a bit:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;As you get more experience in the field, you talk more and code less. The positions you’re in become more about team management than programming. It’s good to remember that software engineering isn’t just coding. Also that with time, your personality changes, and you have to keep adapting.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Team building isn’t about getting a group of people to carry the exact same tasks in the same way. Everyone is different; you’ll have to discover the strengths and weaknesses of each member and figure out how to mix them up. The best teams are often very heterogeneous, and it’s pretty clear to me that this is the case with Tremor as well.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Don’t care about what others say about you (the &lt;em&gt;don’t worry&lt;/em&gt; rule). Don’t let “You’ll never end up being X”, “You’re bad at Y” and similars ever affect you.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Coding is mentally exhausting and burnout is a very common thing. Take good rest, breathe, and have fun. Taking a break from programming is a good idea from time to time.&lt;/p&gt;
&lt;p&gt;I’ve personally experienced burnout myself, so I know this first hand. You may immerse yourself too much in computers or coding (specially under a pandemic that restricts how much you can go out). Finding a hobby outside that is incredibly helpful.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;






















&lt;p&gt;&lt;a name=&quot;_how_tremor_works&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;how-tremor-works&quot; tabindex=&quot;-1&quot;&gt;How Tremor works &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-dynload/#how-tremor-works&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;After starting to write a first prototype for connectors and failing because I didn’t know what I was doing, I decided to step back and try to understand in detail how Tremor works. Once I had that covered, I could try to simplify the plugin system as much as possible in order to keep my sanity.&lt;/p&gt;
&lt;p&gt;I jumped into the codebase of &lt;a href=&quot;https://github.com/tremor-rs/tremor-runtime&quot;&gt;&lt;code&gt;tremor/tremor-runtime&lt;/code&gt;&lt;/a&gt; and tried to figure out how it was structured, also with the help of the team later on. Tremor is loosely based on the actor model. &lt;a href=&quot;https://en.wikipedia.org/wiki/Actor_model&quot;&gt;Quoting Wikipedia&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[The actor model treats the] actor as the universal primitive of concurrent computation. In response to a message it receives, an actor can: make local decisions, create more actors, send more messages, and determine how to respond to the next message received. Actors may modify their own private state, but can only affect each other indirectly through messaging (removing the need for lock-based synchronization).&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It doesn’t use a language (e.g., Erlang) or framework (e.g., &lt;a href=&quot;https://crates.io/crates/bastion&quot;&gt;&lt;code&gt;bastion&lt;/code&gt;&lt;/a&gt;, maybe in the future) that strictly follows the actor model, but it often re-implements the same patterns manually. Tremor is currently implemented with &lt;a href=&quot;https://en.wikipedia.org/wiki/Asynchrony_(computer_programming)&quot;&gt;asynchronous programming&lt;/a&gt;, which means that instead of threads we’ll be working with &lt;em&gt;tasks&lt;/em&gt;, a higher level concept. From &lt;a href=&quot;https://docs.rs/async-std/1.10.0/async_std/task/index.html&quot;&gt;the &lt;a href=&quot;https://crates.io/crates/async-std&quot;&gt;&lt;code&gt;async-std&lt;/code&gt;&lt;/a&gt; documentation&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;An executing asynchronous Rust program consists of a collection of native OS threads, on top of which multiple stackless coroutines are multiplexed. We refer to these as “tasks”. Tasks can be named, and provide some built-in support for synchronization.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;We could summarize this with the sentence “Tremor is based on actors running in separate tasks which communicate asynchronously via channels”. The main actor is called the &lt;code&gt;World&lt;/code&gt;. It contains the state of the program, such as the available artifacts (&lt;em&gt;repositories&lt;/em&gt;) and the running ones (&lt;em&gt;registries&lt;/em&gt;), and it’s used to initialize and control the program.&lt;/p&gt;
&lt;p&gt;I’ll try to follow what Tremor does in order to get a connector running with the help of a few diagrams. The following diagram showcases what happens when a &lt;code&gt;World&lt;/code&gt; is created. This introduces the concept of &lt;em&gt;Managers&lt;/em&gt;, which simply are actors in the system that wrap up some functionality.&lt;/p&gt;
&lt;p&gt;Managers help decouple the communication and the implementation of the underlying functionality. They are also useful to remove some boilerplate when initializing the components, such as creating the communication channel or spawning the component in a separate task.&lt;/p&gt;
&lt;p&gt;Generally, there’s one manager per artefact type, which helps with their initialization process, and then there’s one manager per running instance, handling their communication details.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/blog/plugin-dynload/FfoOBjlG9F-731.avif 731w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/blog/plugin-dynload/FfoOBjlG9F-731.webp 731w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://nullderef.com/blog/plugin-dynload/FfoOBjlG9F-731.png&quot; alt=&quot;Registering diagram&quot; width=&quot;731&quot; height=&quot;541&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Once all the managers are initialized, Tremor currently registers all the built-in artifacts in a “hardcoded” way with &lt;code&gt;register_builtin_types&lt;/code&gt;. But after the plugin system is implemented, this will happen dynamically, i.e., Tremor will automatically look for DLL/SO files in its configured directory and try to register all the plugins it can find. The user may additionally request a specific plugin to be loaded while Tremor is running.&lt;/p&gt;
&lt;p&gt;Note that the initialization of the connectors is done in two steps: first they’re &lt;em&gt;registered&lt;/em&gt;, which just means that the connector is now available for loading (they’re added to the &lt;em&gt;repository&lt;/em&gt;). The connector doesn’t actually start running until a binding is created with it, for example with &lt;code&gt;launch_binding&lt;/code&gt;, which will remove it from the repository and add it to the &lt;em&gt;registry&lt;/em&gt;, with the currently running artifacts.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;connectors::Manager&lt;/code&gt; contains all the connectors running in Tremor, which we’ll now try to understand:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/blog/plugin-dynload/8aUTfRIBFi-731.avif 731w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/blog/plugin-dynload/8aUTfRIBFi-731.webp 731w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://nullderef.com/blog/plugin-dynload/8aUTfRIBFi-731.png&quot; alt=&quot;Initialization diagram&quot; width=&quot;731&quot; height=&quot;541&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Since it’s a multistep process (it’s actually more complicated than registration + creation), the first part of it already provides the tools to initialize the connector (mainly the builder). When the connector needs to start running because it’s been added to a binding in the pipeline, the builder helps to construct it generically with the previously provided configuration details. Finally, it’s moved into a task of its own, so that it may communicate with other parts of Tremor.&lt;/p&gt;
&lt;p&gt;Now that we have a connector running, let’s see how it’s split up into the source and sink parts. In a very similar way, a builder is used to initialize the underlying source, sink, or both, and then a new task is spawned for them.&lt;/p&gt;
&lt;p&gt;A manager is also created for each instance of source/sink, which will handle the communication with other actors. This way, the source and sink interfaces can be kept as simple as possible. These managers will receive connection requests from the pipeline and then redirect or read from it.&lt;/p&gt;
&lt;p&gt;The main difference between sinks and sources currently is that the former can also reply to messages within the same connection. This is useful to acknowledge the package (“Ack”) or to notify something has failed in the sink (“Fail” for a specific event, “CircuitBreaker” to completely stop data from being
sent).&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/blog/plugin-dynload/RPubCh2GFi-811.avif 811w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/blog/plugin-dynload/RPubCh2GFi-811.webp 811w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://nullderef.com/blog/plugin-dynload/RPubCh2GFi-811.png&quot; alt=&quot;Setting up diagram&quot; width=&quot;811&quot; height=&quot;652&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Codecs and preprocessors are involved here both at the source and sink levels. In the source part, the data is transformed or split up through a chain of preprocessors and then the codec is applied. For the sinks, the inverse process is followed: the data is first encoded into bytes with the codec, and then a series of post-processors are applied to the raw binary data.&lt;/p&gt;
&lt;p&gt;Some connectors are based on &lt;em&gt;streams&lt;/em&gt;. They are equivalent for example to TCP streams, which help to group up messages and avoid mixing them up. They are manually started and ended via messages, and the manager saves their state in a field called &lt;code&gt;states&lt;/code&gt; (since for instance preprocessors may need to keep a state). If a connector doesn’t need this, such as the &lt;code&gt;metronome&lt;/code&gt;, it may simply specify &lt;code&gt;DEFAULT_STREAM_ID&lt;/code&gt; as the stream ID always.&lt;/p&gt;
&lt;p&gt;After the full interface of connectors is done, I could implement the two following connector plugins:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/tremor-rs/tremor-runtime/blob/main/src/sink/blackhole.rs&quot;&gt;Blackhole&lt;/a&gt; is used for benchmarking. It takes measurements of the end to end times of each event traversing the pipeline and at the end prints an HDR (High Dynamic Range) &lt;a href=&quot;http://hdrhistogram.org/&quot;&gt;histogram&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/tremor-rs/tremor-runtime/blob/main/src/source/blaster.rs&quot;&gt;Blaster&lt;/a&gt; replays a series of events specified in a file, which is specially useful for performance testing.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Both of these are relatively simple and will be helpful to benchmark the plugin system later on. But that isn’t really important right now; I first need to get it working, and then I can care about performance.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;actual_start&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;taking-a-look-at-ebpf-first&quot; tabindex=&quot;-1&quot;&gt;Taking a look at eBPF first &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-dynload/#taking-a-look-at-ebpf-first&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In the previous articles I mostly considered using either WebAssembly or Dynamic Loading. What I didn’t even know about is &lt;a href=&quot;https://ebpf.io/&quot;&gt;eBPF&lt;/a&gt;, “a revolutionary technology with origins in the Linux kernel that can run sandboxed programs in an operating system kernel”. However, similarly to WebAssembly, its usage has been expanded to user-space applications. eBPF defines a set of bytecode instructions that may be ran by a virtual machine anywhere, similarly to how Wasm works.&lt;/p&gt;
&lt;p&gt;There are multiple active crates for eBPF in Rust. &lt;a href=&quot;https://crates.io/crates/libbpf_rs&quot;&gt;&lt;code&gt;libbpf_rs&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://crates.io/crates/redbpf&quot;&gt;&lt;code&gt;redbpf&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://crates.io/crates/aya&quot;&gt;&lt;code&gt;aya&lt;/code&gt;&lt;/a&gt; are specific to the Linux Kernel. &lt;a href=&quot;https://crates.io/crates/solana_rbpf&quot;&gt;&lt;code&gt;solana_rbpf&lt;/code&gt;&lt;/a&gt; is a virtual machine, so it only works for user-space. The maintainers of the latter use it to &lt;a href=&quot;https://solana.com/&quot;&gt;safely run apps on the blockchain&lt;/a&gt;, and their crate seems to be a fork of the now abandoned (?) &lt;a href=&quot;https://crates.io/crates/rbpf&quot;&gt;&lt;code&gt;rbpf&lt;/code&gt;&lt;/a&gt;. &lt;a href=&quot;https://www.youtube.com/watch?v=xj0PBFjLm1U&amp;amp;t=8701s&quot;&gt;This recent talk at LPC 2021&lt;/a&gt; explains the situation of eBPF in Rust quite well (mainly for Aya, so it’s mostly related to the Linux Kernel).&lt;/p&gt;
&lt;p&gt;Unlike WebAssembly, you don’t necessarily need to serialize or write to an intermediate memory. Since you fully control how the virtual machine works, the runtime could implement a custom sandbox that simply checks for the read/written addresses in the plugins to make sure they aren’t out of bounds, while still sharing the same memory space. So in terms of performance, Tremor itself &lt;em&gt;could&lt;/em&gt; use it — though there’s still the penalty of interpreting plugins instead of running them natively.&lt;/p&gt;
&lt;p&gt;The problem in this case is that, for what I’ve found, Rust support leaves to be desired. Most people seem to use C for eBPF and I think it shows; the number of tutorials/guides/articles about eBPF on Rust is incredibly small. There’s no official target to compile Rust to eBPF, and the only user-space runtime we can use is &lt;code&gt;rbpf&lt;/code&gt; and its derivatives. Looking for information about this topic was somewhat frustrating, specially because the search results are mixed up with kernel-only BPF, which is not relevant to us.&lt;/p&gt;
&lt;p&gt;It doesn’t really seem like the best choice right now, in my opinion. We would have to write almost everything about the plugin system from scratch, including the sandbox itself (allowing only different sets of syscalls, bounds checking, etc). It would be considerably more cumbersome than using something like &lt;code&gt;abi_stable&lt;/code&gt;. Maybe in the future it’d be worth considering it in detail and running some benchmarks, but for now I think dynamic loading is still the clear winner for Tremor. Still, I’m surprised by how flexible eBPF seems to be, and how it’s possible to avoid the memory barrier problem found in Wasm.&lt;/p&gt;
&lt;p&gt;Cheers to Dr. Florentin Rochet for letting me know about this technology — though he’s considering switching to WebAssembly for his project. He’s currently using it to research &lt;em&gt;pluggable&lt;/em&gt; anonymous protocols like Tor, which would allow patches to their code to happen at runtime&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#fn4&quot; id=&quot;fnref4&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt;. This makes it faster to fix vulnerabilities until it’s properly updated upstream, among other things. Pretty cool :)&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_getting_deeper_into_dynamic_linking&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;getting-deeper-into-dynamic-linking&quot; tabindex=&quot;-1&quot;&gt;Getting deeper into dynamic linking &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-dynload/#getting-deeper-into-dynamic-linking&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Now that we definitely know how to approach the plugin system, we have two choices: using raw dynamic linking with the C ABI and &lt;code&gt;libloading&lt;/code&gt;, or trying out the &lt;code&gt;abi_stable&lt;/code&gt; crate. I suggest we do both. We’ll most likely end up using the latter because it should be easier and safer, but it’s still a very good idea to know how &lt;code&gt;abi_stable&lt;/code&gt; works under the hood.&lt;/p&gt;
&lt;p&gt;In the previous article I created a &lt;code&gt;dynamic-simple&lt;/code&gt; experiment in examples to &lt;a href=&quot;https://github.com/marioortizmanero/pdk-experiments&quot;&gt;the pdk-experiments repository&lt;/a&gt;. In this one we’ll try to get an implementation that’s closer to what we need for connectors, so I’ll call the new experiment &lt;a href=&quot;https://github.com/marioortizmanero/pdk-experiments/tree/master/dynamic-connectors&quot;&gt;&lt;code&gt;dynamic-connectors&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_versioning&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;versioning&quot; tabindex=&quot;-1&quot;&gt;Versioning &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-dynload/#versioning&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;In order to get more advanced things running, we should figure out how to properly embed metadata in the plugin. In order to export any type, we already know that it must be FFI-safe. But there’s something else of great importance: versioning. In order to safely load the plugin, one must ensure that the versions of the &lt;code&gt;common&lt;/code&gt; crate match — or at least that they’re compatible — for both the runtime and the plugin. Here’s an example of how this could go wrong if we don’t save information about versioning:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// plugin implementation&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mod&lt;/span&gt; &lt;span class=&quot;token module-declaration namespace&quot;&gt;common&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// This is the declaration for the plugin data in version 0.1&lt;/span&gt;
    &lt;span class=&quot;token attribute attr-name&quot;&gt;#[repr(C)]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;PluginData&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;static&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; new&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;unsafe&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;State&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token attribute attr-name&quot;&gt;#[no_mangle]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;PLUGIN_DATA&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;common&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PluginData&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;common&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PluginData&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;b&quot;test&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    new
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// runtime implementation&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mod&lt;/span&gt; &lt;span class=&quot;token module-declaration namespace&quot;&gt;common&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// And this is the same type, but in version 0.2&lt;/span&gt;
    &lt;span class=&quot;token attribute attr-name&quot;&gt;#[repr(C)]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;PluginData&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u8&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; new&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;unsafe&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;State&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// NOTE: this field is new here!&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; connect&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;unsafe&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;State&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;anyhow&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;unsafe&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; library &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Library&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; library
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;common&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;PluginData&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;b&quot;PLUGIN_DATA&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// !!! UNDEFINED BEHAVIOUR !!! What will `data.connect` be?&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the code above, we can see that, even though both versions of &lt;code&gt;PluginData&lt;/code&gt; are FFI-safe, their layouts aren’t the same, because the last one has a new field. When trying to read &lt;code&gt;PLUGIN_DATA&lt;/code&gt;, undefined behaviour will occur (most likely accessing to an invalid memory address).&lt;/p&gt;
&lt;p&gt;Every plugin should at least export the version of &lt;code&gt;common&lt;/code&gt; it uses, and the runtime should check it before anything else.&lt;/p&gt;
&lt;p&gt;Specifically, the type used to export the version has to be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;FFI-safe&lt;/strong&gt;, so &lt;code&gt;&amp;amp;str&lt;/code&gt; or &lt;code&gt;CStr&lt;/code&gt; are discarded (the latter is a Rust wrapper and not &lt;code&gt;#[repr(C)]&lt;/code&gt;).&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Stable&lt;/strong&gt;. &lt;code&gt;abi_stable::RStr&lt;/code&gt; won’t work either because the versions for &lt;code&gt;abi_stable&lt;/code&gt; might mismatch, since we’re reading the symbol before knowing that. Its layout must be &lt;em&gt;always&lt;/em&gt; the same.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Thread-safe&lt;/strong&gt; (implement &lt;code&gt;Sync&lt;/code&gt;). If we wanted to use something like &lt;code&gt;*const c_char&lt;/code&gt;, the compiler would throw the following error, because it’s a pointer:&lt;/p&gt;
&lt;pre class=&quot;language-plain&quot;&gt;&lt;code class=&quot;language-plain&quot;&gt;error[E0277]: `*const i8` cannot be shared between threads safely
 --&gt; src/lib.rs:4:1
  |
4 | pub static VERSION: *const c_char = b&quot;0.1.0&#92;0&quot;.as_ptr() as _;
  | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `*const i8` cannot be shared between threads safely
  |
  = help: the trait `Sync` is not implemented for `*const i8`
  = note: shared static variables must have a type that implements `Sync`&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Instead, we can use a function that returns the string:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[no_mangle]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;get_version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; c_char &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;b&quot;0.1.0&#92;0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;as_ptr&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;as&lt;/span&gt; _
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Finally, there are multiple ways to handle versioning within the runtime, depending on how fine-grained (but also more error-prone) it should be:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;The simplest way possible: both version strings must be strictly the same.&lt;/li&gt;
&lt;li&gt;The plugin system could take advantage of &lt;a href=&quot;https://semver.org/&quot;&gt;semantic versioning&lt;/a&gt;. Only differences in the major version (X.0.0) would be incompatible. The problem in this case is that this is kept track of manually, and it’s possible that a breaking change is introduced by mistake.&lt;/li&gt;
&lt;li&gt;Since there are actually many kinds of plugins (connectors, codecs, etc), rather than checking the version for the entire &lt;code&gt;common&lt;/code&gt; crate, there could be a version &lt;em&gt;per type of plugin&lt;/em&gt;. If a change in the &lt;code&gt;common&lt;/code&gt; crate only modifies structures for codec plugins, the rest of the plugins would still work.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a name=&quot;_loading_plugins&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;loading-plugins&quot; tabindex=&quot;-1&quot;&gt;Loading plugins &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-dynload/#loading-plugins&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Another complicated topic is plugin distribution and management. In order to make it easier for the user, plugins should be found and loaded automatically. But how exactly should this work? I’ll explain a few ideas.&lt;/p&gt;
&lt;p&gt;Firstly, the plugins can be found automatically by searching one or more user-configurable directories. For instance, in Tremor’s case we could use the environment variable &lt;a href=&quot;https://www.tremor.rs/docs/tremor-query/modules#defaults&quot;&gt;&lt;code&gt;TREMOR_PATH&lt;/code&gt;&lt;/a&gt;. Once we have a list of directories where we should look for plugins there are two ways to do it:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Only checking the immediate files in the directory&lt;/li&gt;
&lt;li&gt;Recursively, which is more convenient but might cause issues if the node is too deep. If the user specified &lt;code&gt;/&lt;/code&gt; as a directory, the runtime would most likely crash unless we used something efficient like &lt;a href=&quot;https://github.com/sharkdp/fd&quot;&gt;&lt;code&gt;fd&lt;/code&gt;&lt;/a&gt; or added a depth limit (which is probably the most sensible choice here).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once we’re traversing a directory, we have to figure out which files are plugins and which aren’t. The easiest way to do it is with file extensions, but this introduces the problem of cross-compatibility. Dynamic libraries usually have a different extension name for each Operating System: Windows uses &lt;code&gt;.dll&lt;/code&gt;, Linux and FreeBSD use &lt;code&gt;.so&lt;/code&gt;, and macOS uses &lt;code&gt;.dylib&lt;/code&gt;, as specified by &lt;a href=&quot;https://doc.rust-lang.org/std/env/consts/constant.DLL_EXTENSION.html&quot;&gt;&lt;code&gt;std::env::consts::DLL_EXTENSION&lt;/code&gt;&lt;/a&gt;. It would make sense that our runtime only tried to load plugins with their respective extensions.&lt;/p&gt;
&lt;p&gt;However, these extensions are just conventions; we could just enforce a single extension name, as &lt;a href=&quot;https://docs.rs/libloading/0.7.0/libloading/struct.Library.html#tips&quot;&gt;&lt;code&gt;libloading&lt;/code&gt; suggests&lt;/a&gt;. It might be easier if we just used &lt;code&gt;.module&lt;/code&gt; for everything, for example. In order to make them even more convenient, it’d be nice if they also worked for all of these Operating Systems within a single file. Apparently, this is called a &lt;a href=&quot;https://en.wikipedia.org/wiki/Fat_binary&quot;&gt;“Fat binary”&lt;/a&gt; and it was used in the past, but it’d be extremely complicated to get working now&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#fn5&quot; id=&quot;fnref5&quot;&gt;[5]&lt;/a&gt;&lt;/sup&gt;, so we’ll just forget about it.&lt;/p&gt;
&lt;p&gt;Additionally, the Tremor plugin system requires that plugins can be loaded &lt;em&gt;both at initialization time and at runtime&lt;/em&gt;. There is a decision to be made in here about how the latter should work:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Manually: after adding the new plugin to the configured directories (or specifying its full path), the user would input in some way that it should be loaded (for example with the CLI tool).&lt;/li&gt;
&lt;li&gt;Automatically: the runtime could detect whenever a new plugin is added to the list with a crate like &lt;a href=&quot;https://crates.io/crates/notify&quot;&gt;&lt;code&gt;notify&lt;/code&gt;&lt;/a&gt;. Most Operating Systems have some way to get a notification whenever a file or directory changes. In case a new file was added to any of the configured directories, the runtime could try to load it. This way, it’d work with no user interaction, other than adding the file to one of the directories.&lt;/li&gt;
&lt;li&gt;A combination of both: if the directories configured to look for plugins can’t be changed at runtime it might be interesting to also let the user manually load plugins in specific paths.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a name=&quot;_handling_state&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;handling-state&quot; tabindex=&quot;-1&quot;&gt;Handling state &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-dynload/#handling-state&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Most plugins will want to keep some kind of state between calls to its interface. For example, the TCP connector will need to keep its socket after its initialization in order to send or receive messages. This means that most of them will follow the following pattern:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; plugin&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
plugin&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;something&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The state is first created with a &lt;code&gt;new&lt;/code&gt; function that initializes everything as needed, and then a mutable reference is passed to its functions. The main problem here is, if each plugin is going to have its own type of state, what’s the function signature of &lt;code&gt;Plugin::something&lt;/code&gt;, defined in &lt;code&gt;common&lt;/code&gt;?&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_generics_in_plugins&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;generics-in-plugins&quot; tabindex=&quot;-1&quot;&gt;Generics in plugins? &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-dynload/#generics-in-plugins&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In a regular Rust project we’d just make &lt;code&gt;Plugin::something&lt;/code&gt; generic over a common trait that all states should implement. Unfortunately, generics in plugins are fundamentally impossible. In Rust, monomorphization turns generic code into specific code by filling in the concrete types that are used when &lt;strong&gt;compiled&lt;/strong&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#fn6&quot; id=&quot;fnref6&quot;&gt;[6]&lt;/a&gt;&lt;/sup&gt;. Plugins are loaded at runtime, so they may want to use types the compiler didn’t generate code for.&lt;/p&gt;
&lt;p&gt;It’s really easy to prove in Rust with the following example. We’ll try to &lt;em&gt;load&lt;/em&gt; an external function with generics:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This results in the following error:&lt;/p&gt;
&lt;pre class=&quot;language-plain&quot;&gt;&lt;code class=&quot;language-plain&quot;&gt;error[E0044]: foreign items may not have type parameters
 --&gt; src/lib.rs:2:5
  |
2 |     fn foo&amp;lt;T&gt;(_: T);
  |     ^^^^^^^^^^^^^^^^ can&#39;t have type parameters
  |
  = help: replace the type parameters with concrete types like `u32`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0044`.&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Interestingly enough, the compiler lets you export generic functions declared &lt;em&gt;in Rust&lt;/em&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This confused me in the beginning; it made me think generic functions through FFI were somehow be possible. But as described in &lt;a href=&quot;https://github.com/rust-lang/rust/pull/15831&quot;&gt;the original issue that allowed them&lt;/a&gt;, they’re only supported to pass callbacks to C functions.&lt;/p&gt;
&lt;p&gt;Note that generics in plugins do work for lifetimes. This will compile:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;C&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Even though lifetimes and generics share the same syntax, in the case of lifetimes they are only annotations for the Rust compiler; monomorphization is not applied.&lt;/p&gt;
&lt;p&gt;If you want to know more about this topic, I’d suggest watching &lt;a href=&quot;https://www.youtube.com/watch?v=xcygqF5LVmM&quot;&gt;this video by Jon Gjengset&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_dyn_in_plugins&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;dyn-in-plugins&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;dyn&lt;/code&gt; in plugins? &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-dynload/#dyn-in-plugins&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The alternative to generics is often using trait object types with &lt;code&gt;dyn&lt;/code&gt;. Again, will that work for plugins? Let’s try:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;trait&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;PluginState&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;foo&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;_&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;dyn&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;PluginState&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Compiling…&lt;/p&gt;
&lt;pre class=&quot;language-plain&quot;&gt;&lt;code class=&quot;language-plain&quot;&gt;warning: `extern` fn uses type `dyn PluginState`, which is not FFI-safe
 --&gt; src/lib.rs:2:25
  |
2 | pub extern fn foo&amp;lt;T&gt;(_: &amp;amp;dyn PluginState) {}
  |                         ^^^^^^^^^^^^^^^^ not FFI-safe
  |
  = note: `#[warn(improper_ctypes_definitions)]` on by default
  = note: trait objects have no C equivalent&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Nope. &lt;code&gt;dyn&lt;/code&gt; is strictly part of the Rust ABI, so it’s not stable for our plugin system.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_the_c_way&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;the-c-way&quot; tabindex=&quot;-1&quot;&gt;The C way &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-dynload/#the-c-way&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There are two popular ways to approach this in C:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Globals, but they are hard to deal with in concurrent programs&lt;/li&gt;
&lt;li&gt;&lt;code&gt;void*&lt;/code&gt;, which is a pointer with no associated type&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#fn7&quot; id=&quot;fnref7&quot;&gt;[7]&lt;/a&gt;&lt;/sup&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;For safety’s sake, let’s see how the second one works. This pattern is used for example in PulseAudio&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#fn8&quot; id=&quot;fnref8&quot;&gt;[8]&lt;/a&gt;&lt;/sup&gt;, in which callbacks pass a &lt;code&gt;void*&lt;/code&gt; parameter for user data. Here’s a simpler program:&lt;/p&gt;
&lt;pre class=&quot;language-c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;stdio.h&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;stdlib.h&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// The state of the plugin&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; counter&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;plugin_state_t&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Exported by the plugin, initializes the state&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;plugin_state_t&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; plugin_state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;malloc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;plugin_state_t&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    plugin_state&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;counter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; plugin_state&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Exported by the plugin, which takes a pointer to its state&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;something&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// We know the runtime used `new` to initialize the state, so we can cast it&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// back to its original type.&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;plugin_state_t&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; plugin_state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;plugin_state_t&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token function&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Current state: { counter = %d }&#92;n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; plugin_state&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    plugin_state&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;counter&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Final state: { counter = %d }&#92;n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; plugin_state&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// We initialize the plugin, which returns its state&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// When calling anything from the plugin we pass its state&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;something&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Don&#39;t forget!&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This does work perfectly, and we could port it to Rust as a straightforward solution. However, it has the following inconvenients:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;It’s very &lt;code&gt;unsafe&lt;/code&gt;. We’d need to add some kind of wrapper/macro for the plugin developers to avoid invoking undefined behaviour.&lt;/li&gt;
&lt;li&gt;We know nothing about the state. A &lt;code&gt;void*&lt;/code&gt; can’t enforce &lt;code&gt;Debug&lt;/code&gt; being implemented, nor any base other method or trait that might be of interest to us.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Based on how this works, we can try to extend it by implementing intheritance-based polymorphism manually. &lt;a href=&quot;https://adventures.michaelfbryan.com/posts/ffi-safe-polymorphism-in-rust/&quot;&gt;This blog post by Michael F. Bryan’s&lt;/a&gt; covers the topic extremely well.&lt;/p&gt;
&lt;p&gt;Here’s how our previous example would look like, which could be translated to Rust with no problems whatsoever:&lt;/p&gt;
&lt;pre class=&quot;language-c&quot;&gt;&lt;code class=&quot;language-c&quot;&gt;&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;stdio.h&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token macro property&quot;&gt;&lt;span class=&quot;token directive-hash&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;token directive keyword&quot;&gt;include&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&amp;lt;stdlib.h&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// The base plugin type&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;base_state_t&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;print&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;base_state_t&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;base_state_t&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// The state of the plugin, child of the above type&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;typedef&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;base_state_t&lt;/span&gt; base&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; counter&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;plugin_state_t&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// The implementation of `print` for the `plugin_state_t` child&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;base_state_t&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;plugin_state_t&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; plugin_state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;plugin_state_t&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;printf&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Current state: { counter = %d }&#92;n&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; plugin_state&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;counter&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Exported by the plugin, initializes the state&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;base_state_t&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;base_state_t&lt;/span&gt; base &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;print&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;plugin_state_t&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; plugin_state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;malloc&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;sizeof&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;plugin_state_t&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    plugin_state&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;base &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; base&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    plugin_state&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;counter &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;base_state_t&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; plugin_state&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;// Exported by the plugin, which takes a pointer to its state&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;something&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// We know the runtime used `new` to initialize the state, so we can cast it&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// back to its original type.&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;plugin_state_t&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; plugin_state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;plugin_state_t&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    plugin_state&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;counter&lt;span class=&quot;token operator&quot;&gt;++&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// We initialize the plugin, which returns its state&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;base_state_t&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// When calling anything from the plugin we pass its state&lt;/span&gt;
    state&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;something&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    state&lt;span class=&quot;token operator&quot;&gt;-&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Don&#39;t forget!&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;free&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The main difference in the code is the new base class &lt;code&gt;plugin_base_t&lt;/code&gt;. It defines a single function &lt;code&gt;print&lt;/code&gt; that should be implemented by its children, and it could also include other fields that would be inherited. Casting between &lt;code&gt;base_state_t&lt;/code&gt; and &lt;code&gt;plugin_base_t&lt;/code&gt; is explicitly allowed by the C standard as long as the base class is the first member in the struct, so this is sound.&lt;/p&gt;
&lt;p&gt;This covers all of our necessities. The only remaining problem is that it’s still quite unsafe to use. Thankfully, we can avoid most user errors by using the crate &lt;a href=&quot;https://crates.io/crates/thin_trait_object&quot;&gt;&lt;code&gt;thin_trait_object&lt;/code&gt;&lt;/a&gt;, which provides a very flexible procedural macro to automatically write all the necessary boilerplate in Rust.&lt;/p&gt;



&lt;p&gt;&lt;a name=&quot;error_handling&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;error-handling&quot; tabindex=&quot;-1&quot;&gt;Error Handling &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-dynload/#error-handling&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’ve created a few more plugins to see how this approach reacts to some common errors. Since in the end we aren’t using a sandbox, I wonder what kind of errors we &lt;em&gt;can’t&lt;/em&gt; recover from.&lt;/p&gt;
&lt;p&gt;The full source for the example that’s supported to work is &lt;a href=&quot;https://github.com/marioortizmanero/pdk-experiments/tree/master/dynamic-connectors/plugin-metronome&quot;&gt;here&lt;/a&gt;. Let’s see a few ways in which the plugin could go wrong:&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_missing_fields&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;missing-fields&quot; tabindex=&quot;-1&quot;&gt;Missing fields &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-dynload/#missing-fields&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The &lt;code&gt;plugin-missing&lt;/code&gt; directory contains an empty plugin. It doesn’t export any fields at all, like the name or the version. This one is already handled by &lt;code&gt;libloading&lt;/code&gt;, actually. When using &lt;code&gt;library.get(&amp;quot;name&amp;quot;)&lt;/code&gt;, if “name” is not exported by the shared object, the following error will show up. &lt;a href=&quot;https://github.com/marioortizmanero/pdk-experiments/tree/master/dynamic-connectors/plugin-missing&quot;&gt;See the full code here&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-plain&quot;&gt;&lt;code class=&quot;language-plain&quot;&gt;$ make debug-missing
Error when setting up the plugin: plugin-missing/target/debug/libplugin_missing.so: undefined symbol: get_name&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a name=&quot;_version_mismatch&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;version-mismatch&quot; tabindex=&quot;-1&quot;&gt;Version mismatch &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-dynload/#version-mismatch&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;After implementing the versioning system, we can see how these kinds of errors can be caught safely. &lt;a href=&quot;https://github.com/marioortizmanero/pdk-experiments/tree/master/dynamic-connectors/plugin-versionmismatch&quot;&gt;See the full code here&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-plain&quot;&gt;&lt;code class=&quot;language-plain&quot;&gt;$ make debug-versionmismatch
Initializing plugin versionmismatch
Version mismatch. Aborting.
Error when setting up the plugin: version mismatch: 0.0.0 incompatible with 0.1.0&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a name=&quot;_wrong_type&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;wrong-type&quot; tabindex=&quot;-1&quot;&gt;Wrong type &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-dynload/#wrong-type&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Libloading assumes the type that’s being loaded is correct. If for example the plugin exported the &lt;code&gt;get_name&lt;/code&gt; function, but it returned an integer instead of a string, we’d be in &lt;a href=&quot;https://docs.rs/libloading/latest/libloading/struct.Library.html#safety-1&quot;&gt;undefined-behaviour-land&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Users of this API must specify the correct type of the function or variable loaded. Using a &lt;code&gt;Symbol&lt;/code&gt; with a wrong type is undefined.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Ignoring this will cause an unavoidable segfault. &lt;a href=&quot;https://github.com/marioortizmanero/pdk-experiments/tree/master/dynamic-connectors/plugin-wrongtype&quot;&gt;See the full code here&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-plain&quot;&gt;&lt;code class=&quot;language-plain&quot;&gt;$ make debug-wrongtype
Segmentation fault (core dumped)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a name=&quot;_wrong_address&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;wrong-address&quot; tabindex=&quot;-1&quot;&gt;Wrong address &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-dynload/#wrong-address&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Unfortunately, there’s not much we can do about out of bounds pointers. If the plugin exports, e.g., the name with a null pointer, we’ll just get a segmentation fault. &lt;a href=&quot;https://github.com/marioortizmanero/pdk-experiments/tree/master/dynamic-connectors/plugin-wrongaddress&quot;&gt;See the full code here&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-plain&quot;&gt;&lt;code class=&quot;language-plain&quot;&gt;$ make debug-wrongaddress
Segmentation fault (core dumped)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In order to avoid this, the runtime could manually check that the pointer isn’t zero, the usual value for null. But the same would still happen if the pointer’s value was 1 instead of 0. And even if it was within bounds, it could just point to garbage anyway.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_panicking&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;panicking&quot; tabindex=&quot;-1&quot;&gt;Panicking &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-dynload/#panicking&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: This seems to be Work in Progress, and panicking through the FFI boundary will be allowed after [&lt;code&gt;#![feature(c_unwind)](https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html)&lt;/code&gt;] is implemented.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Panicking is not supported in the C ABI; it’s considered undefined behaviour&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#fn9&quot; id=&quot;fnref9&quot;&gt;[9]&lt;/a&gt;&lt;/sup&gt;. If a plugin panics, the entire program will most likely abort. Plugin developers should wrap every single exported function in &lt;a href=&quot;https://doc.rust-lang.org/std/panic/fn.catch_unwind.html&quot;&gt;&lt;code&gt;catch_unwind&lt;/code&gt;&lt;/a&gt; in order to not crash the entire runtime when something goes wrong. &lt;a href=&quot;https://github.com/marioortizmanero/pdk-experiments/tree/master/dynamic-connectors/plugin-panic&quot;&gt;See the full code here&lt;/a&gt;.&lt;/p&gt;
&lt;pre class=&quot;language-plain&quot;&gt;&lt;code class=&quot;language-plain&quot;&gt;$ make debug-panic
Segmentation fault (core dumped)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a name=&quot;_full_implementation&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;full-implementation&quot; tabindex=&quot;-1&quot;&gt;Full implementation &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-dynload/#full-implementation&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The example at &lt;a href=&quot;https://github.com/marioortizmanero/pdk-experiments/tree/master/dynamic-connectors&quot;&gt;&lt;code&gt;dynamic-connectors&lt;/code&gt;&lt;/a&gt; approaches the topics covered in this section in the simplest of ways, while still implementing a working plugin system. More specifically:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Versioning requires an exact match between the version of &lt;code&gt;common&lt;/code&gt; in the plugin and the runtime.&lt;/li&gt;
&lt;li&gt;The plugins are manually loaded given a directory.&lt;/li&gt;
&lt;li&gt;The runtime looks for plugins in the immediate files of the directory, i.e., non-recursively.&lt;/li&gt;
&lt;li&gt;The state is passed as a void pointer, rather than trying to use inheritance.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Most of these are just decisions to be made by the designer of the system. I chose to go for the easiest options so that we can focus on &lt;code&gt;abi_stable&lt;/code&gt; sooner.&lt;/p&gt;
&lt;p&gt;I did implement a declarative macro to make plugin-writing easier and less error-prone, just to see how it’d work. It takes care of most of the boilerplate, which basically consists on creating functions for the name, kind, and version returning &lt;code&gt;*const c_char&lt;/code&gt;, and exporting the plugin data struct. For the curious, it’s defined in the &lt;code&gt;common&lt;/code&gt; directory.&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// sample usage of the macro&lt;/span&gt;
&lt;span class=&quot;token macro property&quot;&gt;define_connector_plugin!&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;metronome&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ConnectorPlugin&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        new&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        something&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        is_sink&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        is_source&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The plugin system supports multiple types of plugins (connectors, codecs, etc), so there’s actually a specific macro and data structure for each of them.&lt;/p&gt;
&lt;p&gt;Apart from the examples listed in the &lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#error_handling&quot;&gt;“Error Handling”&lt;/a&gt; section, I’ve created a proper plugin that is supposed to work, with the name &lt;code&gt;plugin-metronome&lt;/code&gt;. It was supposed to implement the &lt;a href=&quot;https://www.tremor.rs/docs/artefacts/onramps#metronome&quot;&gt;&lt;code&gt;metronome&lt;/code&gt; connector&lt;/a&gt;, but I’ve decided to just leave that for the next post. I’d rather spend my time writing the real-life example with the &lt;code&gt;abi_stable&lt;/code&gt; version, because it’s what we’ll end up using.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_conclusion&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-dynload/#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This article has covered a lot of questions that one may encounter when trying to use dynamic loading for a plugin system. It’s definitely a complicated task with lots of decisions to make, and plenty of pitfalls. This is why I’d love to try &lt;code&gt;abi_stable&lt;/code&gt; in detail, which would let us do the same things but without a line of &lt;code&gt;unsafe&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;In my opinion, although &lt;code&gt;abi_stable&lt;/code&gt; is a very large crate and somewhat hard to learn, most of the problems this post exposes are greatly simplified thanks to it. In the next article I’ll see the differences between both approaches.&lt;/p&gt;

&lt;script src=&quot;https://f.convertkit.com/ckjs/ck.5.js&quot;&gt;&lt;/script&gt;
&lt;form action=&quot;https://app.kit.com/forms/7534343/subscriptions&quot; class=&quot;seva-form formkit-form&quot; method=&quot;post&quot; data-sv-form=&quot;7534343&quot; data-uid=&quot;3680381778&quot; data-format=&quot;inline&quot; data-version=&quot;5&quot; data-options=&#39;{&quot;settings&quot;:{&quot;after_subscribe&quot;:{&quot;action&quot;:&quot;message&quot;,&quot;success_message&quot;:&quot;Success! Check your email to confirm your subscription. If you can’t find it, look in the spam folder.&quot;,&quot;redirect_url&quot;:&quot;&quot;},&quot;analytics&quot;:{&quot;google&quot;:null,&quot;fathom&quot;:null,&quot;facebook&quot;:null,&quot;segment&quot;:null,&quot;pinterest&quot;:null,&quot;sparkloop&quot;:null,&quot;googletagmanager&quot;:null},&quot;modal&quot;:{&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;powered_by&quot;:{&quot;show&quot;:true,&quot;url&quot;:&quot;https://kit.com/features/forms?utm_campaign=poweredby&amp;amp;utm_content=form&amp;amp;utm_medium=referral&amp;amp;utm_source=dynamic&quot;},&quot;recaptcha&quot;:{&quot;enabled&quot;:false},&quot;return_visitor&quot;:{&quot;action&quot;:&quot;show&quot;,&quot;custom_content&quot;:&quot;&quot;},&quot;slide_in&quot;:{&quot;display_in&quot;:&quot;bottom_right&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;sticky_bar&quot;:{&quot;display_in&quot;:&quot;top&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15}},&quot;version&quot;:&quot;5&quot;}&#39; min-width=&quot;400 500 600 700 800&quot;&gt;
  &lt;div data-style=&quot;clean&quot;&gt;
    &lt;div class=&quot;formkit-header&quot; data-element=&quot;header&quot;&gt;
      &lt;h2&gt;Want to receive an email when I post?&lt;/h2&gt;
    &lt;/div&gt;
    &lt;ul class=&quot;formkit-alert formkit-alert-error&quot; data-element=&quot;errors&quot; data-group=&quot;alert&quot;&gt;&lt;/ul&gt;
    &lt;div data-element=&quot;fields&quot; data-stacked=&quot;false&quot; class=&quot;seva-fields formkit-fields&quot;&gt;
      &lt;div class=&quot;formkit-field&quot;&gt;
        &lt;input class=&quot;formkit-input&quot; name=&quot;email_address&quot; aria-label=&quot;Email Address&quot; placeholder=&quot;Email Address&quot; required=&quot;&quot; type=&quot;email&quot;&gt;
      &lt;/div&gt;
      &lt;button data-element=&quot;submit&quot; class=&quot;formkit-submit&quot; style=&quot;background-color:#e06c75;font-weight:700&quot;&gt;
        &lt;div class=&quot;formkit-spinner&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span class=&quot;&quot;&gt;Subscribe&lt;/span&gt;
      &lt;/button&gt;
    &lt;/div&gt;
    &lt;div class=&quot;formkit-guarantee&quot; data-element=&quot;guarantee&quot;&gt;
      &lt;p&gt;&lt;em&gt;Free and without spam. You can also use my &lt;a href=&quot;https://nullderef.com/index.xml&quot; target=&quot;_blank&quot;&gt;RSS&lt;/a&gt;, &lt;a href=&quot;https://x.com/null_deref&quot; target=&quot;_blank&quot;&gt;X&lt;/a&gt;, or &lt;a href=&quot;https://mstdn.social/@nullderef&quot; target=&quot;_blank&quot;&gt;Mastodon&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
      &lt;p style=&quot;opacity:.6;font-size:16px&quot;&gt;&lt;em&gt;Powered by &lt;a href=&quot;https://kit.com/&quot; target=&quot;_black&quot;&gt;Kit&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;style&gt;.formkit-form[data-uid=&quot;3680381778&quot;] a{text-decoration:underline}.formkit-form[data-uid=&quot;3680381778&quot;] legend{border:none;font-size:inherit;margin-bottom:10px;padding:0;position:relative;display:table}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-checkboxes,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-select{width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{border:0;border-radius:var(--radius);color:#fff;cursor:pointer;display:inline-block;text-align:center;font-weight:500;cursor:pointer;margin-bottom:15px;overflow:hidden;padding:0;position:relative;vertical-align:middle}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover{outline:0}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover&gt;span{background-color:rgba(0,0,0,.1)}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit&gt;span{display:block;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out;padding:12px 24px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input{background:#fff;padding:12px;border:1px solid var(--border);border-radius:var(--radius);-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;line-height:1.4;margin:0;-webkit-transition:border-color ease-out .3s;transition:border-color ease-out .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:focus{outline:0;border-color:#1677be;-webkit-transition:border-color ease .3s;transition:border-color ease .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-webkit-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-moz-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:-ms-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert{background:#f9fafb;border:1px solid #e3e3e3;border-radius:5px;-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;list-style:none;margin:25px auto;padding:12px;text-align:center;width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert:empty{display:none}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-success{background:#d3fbeb;border-color:#10bf7a;color:#0c905c}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-error{background:#fde8e2;border-color:#f2643b;color:#ea4110}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;height:0;width:0;margin:0 auto;position:absolute;top:0;left:0;right:0;width:0;overflow:hidden;text-align:center;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div{margin:auto;width:12px;height:12px;background-color:#fff;opacity:.3;border-radius:100%;display:inline-block;-webkit-animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both;animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:first-child{-webkit-animation-delay:-.32s;animation-delay:-.32s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:nth-child(2){-webkit-animation-delay:-.16s;animation-delay:-.16s}.formkit-form[data-uid=&quot;3680381778&quot;]{border:1px solid var(--border);border-radius:var(--radius);position:relative;overflow:hidden;background:var(--code-bg)}.formkit-form[data-uid=&quot;3680381778&quot;]{padding:20px;width:100%;position:relative}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-header{margin:0 0 27px 0;font-size:20px;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee{margin-top:10px;margin-bottom:32px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee&gt;p{margin:0;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-fields{display:flex;flex-wrap:wrap;margin:25px auto 0 auto;gap:10px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-field,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{flex:1 0 100%}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;]{padding:0 30px}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-field{flex:100 1 auto}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-submit{flex:1 1 auto}&lt;/style&gt;
&lt;/form&gt;
&lt;h2 id=&quot;footnotes&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://pluginized-protocols.org/&quot;&gt;Pluginized Protocols&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://pquic.org/&quot;&gt;Pluginized QUIC&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://petsymposium.org/2019/files/hotpets/proposals/rochet-fan.pdf&quot;&gt;Flexible Anonymous Network (paper)&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn4&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://petsymposium.org/2019/files/hotpets/slides/rochet-fan-slides.pdf&quot;&gt;Flexible Anonymous Network (slides)&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#fnref4&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn5&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://stackoverflow.com/a/63346857/11488352&quot;&gt;(C/C++) How to generate executable file that can run on both Windows and Linux? — StackOverflow&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#fnref5&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn6&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://doc.rust-lang.org/book/ch10-01-syntax.html#performance-of-code-using-generics&quot;&gt;Performance of Code Using Generics — The Rust Programming Language&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#fnref6&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn7&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.learncpp.com/cpp-tutorial/void-pointers/&quot;&gt;Void pointers — Learn C++&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#fnref7&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn8&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://freedesktop.org/software/pulseaudio/doxygen/stream_8h.html#a2dcc985c65964da290a0c2e1bf103175&quot;&gt;&lt;code&gt;pa_stream_set_write_callback&lt;/code&gt; — PulseAudio docs&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#fnref8&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn9&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://doc.rust-lang.org/nomicon/ffi.html#ffi-and-panics&quot;&gt;FFI and panics — Rustonomicon&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-dynload/#fnref9&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</content>
  </entry>
  <entry>
    <title>Plugins in Rust: Getting Started</title>
    <link href="https://nullderef.com/blog/plugin-start/" />
    <updated>2021-09-05T00:00:00Z</updated>
    <id>https://nullderef.com/blog/plugin-start/</id>
    <content type="html">&lt;details class=&quot;toc&quot;&gt;
&lt;summary&gt;Table of Contents&lt;/summary&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-start/#about-tremor&quot;&gt;About Tremor &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-start/#getting-to-know-the-team&quot;&gt;Getting to know the team &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-start/#getting-to-know-other-mentees&quot;&gt;Getting to know other mentees &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-start/#learning-about-the-internals-of-tremor&quot;&gt;Learning about the internals of Tremor &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-start/#the-first-steps&quot;&gt;The first steps &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-start/#experimenting-with-dynamic-loading&quot;&gt;Experimenting with dynamic loading &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-start/#abi-unstability-it-s-much-worse-than-it-seems&quot;&gt;ABI unstability, it’s much worse than it seems &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-start/#getting-a-simple-example-running&quot;&gt;Getting a simple example running &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-start/#generating-bindings&quot;&gt;Generating bindings &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-start/#crate-type-values&quot;&gt;crate-type values &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-start/#rlib-files&quot;&gt;rlib files &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-start/#can-we-use-webassembly-for-this&quot;&gt;Can we use WebAssembly for this? &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-start/#webassembly-targets&quot;&gt;WebAssembly targets &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-start/#wasmer-vs-wasmer-runtime&quot;&gt;wasmer vs wasmer_runtime &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-start/#more-complex-types&quot;&gt;More complex types &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-start/#wrapping-up&quot;&gt;Wrapping up &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/details&gt;
&lt;p&gt;Welcome to the second article of my &lt;a href=&quot;https://nullderef.com/series/rust-plugins/&quot;&gt;“Plugins in Rust” series&lt;/a&gt;! Here I will try to actually write some simple code of what a plugin system might look like, and try to answer any questions that may arise while doing so.&lt;/p&gt;
&lt;p&gt;Even though the project is specific to &lt;a href=&quot;https://www.tremor.rs/&quot;&gt;Tremor&lt;/a&gt;, I try to keep it relatively generic so that it’s useful to future rustaceans interested in plugin systems. So if you don’t really care about Tremor’s specific case, you can skip to the &lt;a href=&quot;https://nullderef.com/blog/plugin-start/#actual_start&quot;&gt;next section&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_about_tremor&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;about-tremor&quot; tabindex=&quot;-1&quot;&gt;About Tremor &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-start/#about-tremor&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a name=&quot;_getting_to_know_the_team&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;getting-to-know-the-team&quot; tabindex=&quot;-1&quot;&gt;Getting to know the team &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-start/#getting-to-know-the-team&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;My first official meeting with the Tremor team took place in the beginnings of August. I had already been lurking in their Discord server before, and I had talked with them a few times, so I more or less knew what to expect. But I was still surprised by their welcomingness and how refreshing their dynamics were.&lt;/p&gt;
&lt;p&gt;Starting to work on Tremor for the first time with such a large task at first sounds daunting. I was somewhat overwhelmed by that and felt quite some pressure on me. But their words helped me de-stress about it; finishing the project would of course be hard, but no need to worry about things you can’t control yet.&lt;/p&gt;
&lt;p&gt;I particularly liked the three “rules” of their workflow:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Have fun&lt;/li&gt;
&lt;li&gt;Take good notes&lt;/li&gt;
&lt;li&gt;Don’t worry&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;My mentors were Matthias, Darach, and Heinz. They seemed like very different kinds of people; something they knew and took advantage of. Decision-taking is hard; it requires good communication and an open mind. I also took note of this quote (roughly) from them:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;When I have an idea about something, I just post it on the Discord server and wait for it to be destroyed by someone else. If it doesn’t, maybe it’s worth considering.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p style=&quot;margin-top:-15px&quot;&gt;
&lt;i&gt;— Darach&lt;/i&gt;
&lt;/p&gt;
&lt;p&gt;Also note that while writing this post I quite literally broke my leg (tibia &amp;amp; fibula). The team was very supportive, and I took some rest until I went through surgery. Which is another reason as to why this article has taken me longer than usual to release. Thanks a lot to all of them ❤️.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_getting_to_know_other_mentees&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;getting-to-know-other-mentees&quot; tabindex=&quot;-1&quot;&gt;Getting to know other mentees &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-start/#getting-to-know-other-mentees&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;In the first reunion I was suggested to meet the other LFX mentee at that moment, @aryan9600 on Discord, which seemed like a good idea. I’d seen him a couple of times and knew he’d been contributing to Tremor even before the mentorship.&lt;/p&gt;
&lt;p&gt;Aryan turned out to be a very nice colleague! He was very enthusiastic about the project, and he explained me his progress in detail. In summary, he was starting to add gRPC support to Tremor, and wasn’t sure how to approach it so that new interfaces could be added outside the compilation time. He had multiple ideas on how to implement this, but the one that interested me the most was via Wasm or dynamic linking, which was exactly what I was trying to do as well for the system. This meant I’d probably spend more time with him, as our projects were more interconnected than I thought.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_learning_about_the_internals_of_tremor&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;learning-about-the-internals-of-tremor&quot; tabindex=&quot;-1&quot;&gt;Learning about the internals of Tremor &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-start/#learning-about-the-internals-of-tremor&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I’ve always known that the original intent for the system was the typical architecture via dynamic linking, but I wanted to explore the viability of Wasm or other alternatives before ruling it out. Would this idea be destroyed by someone in the end, or does it stand a chance? In order to know that I’d first need to find more specific requisites by defining the basic interfaces of the system. Which is very hard at first because you need to know more about the internals of Tremor and how it works.&lt;/p&gt;
&lt;p&gt;Luckily, Matthias spent the second meeting explaining to me how Tremor works, with the help of some drawings, taking a look at the code, and running it. He also taught me the novel concept of a &lt;em&gt;connector&lt;/em&gt;, which is just an abstraction over both sources and sinks. The thing was that connectors were still Work In Progress, and Matthias needed to know how the system would work to modify the &lt;code&gt;Connector&lt;/code&gt; interface. Mainly how to combine plugins and generics, which is what I’ll try to figure out in this article.&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/blog/plugin-start/ZU3KdUwui4-1341.avif 1341w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/blog/plugin-start/ZU3KdUwui4-1341.webp 1341w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://nullderef.com/blog/plugin-start/ZU3KdUwui4-1341.png&quot; alt=&quot;Connectors diagram&quot; width=&quot;1341&quot; height=&quot;654&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Back when I talked to Aryan we ended up asking ourselves the same exact things. Turns out that the generated code with &lt;code&gt;tonic&lt;/code&gt; for gRPC uses generics, which might not be compatible with dynamic loading.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;actual_start&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-first-steps&quot; tabindex=&quot;-1&quot;&gt;The first steps &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-start/#the-first-steps&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Up until now I had mostly investigated about the &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/&quot;&gt;technologies&lt;/a&gt; that could be used for the system, but I was suggested that I should first take a look at how the interfaces would actually look like. That way it would be easier to take the final decision for the system’s technology, according to the requirements for these interfaces. In summary, the following questions arose:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;What’s the performance hit of the system?&lt;/li&gt;
&lt;li&gt;Can we avoid serialization?&lt;/li&gt;
&lt;li&gt;Is it possible to use generics in the system?&lt;/li&gt;
&lt;li&gt;Otherwise, is it possible to use &lt;code&gt;dyn&lt;/code&gt; in the system?&lt;/li&gt;
&lt;li&gt;If all else fails, what can be done to represent a “generic” trait object in
the interface Tremor exports for plugins?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This meant I had to do more research. I spent a few days reading different articles and forum discussions, but it’s a rabbit hole 🐰! A few links I recommend checking out:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://fasterthanli.me/articles/so-you-want-to-live-reload-rust&quot;&gt;So you want to live-reload Rust — fasterthanli.me&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://adventures.michaelfbryan.com/posts/wasm-as-a-platform-for-abstraction/&quot;&gt;WebAssembly as a Platform for Abstraction — adventures.michaelfbryan.com&lt;/a&gt;, recommended to me by Aryan&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://adventures.michaelfbryan.com/posts/ffi-safe-polymorphism-in-rust/&quot;&gt;FFI-Safe Polymorphism: Thin Trait Objects — adventures.michaelfbryan.com&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Michael F. Bryan has put a lot of effort into teaching how dynamic loading and Wasm work in Rust. Thanks a lot to him!&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_experimenting_with_dynamic_loading&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;experimenting-with-dynamic-loading&quot; tabindex=&quot;-1&quot;&gt;Experimenting with dynamic loading &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-start/#experimenting-with-dynamic-loading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;&lt;a name=&quot;_abi_unstability_its_much_worse_than_it_seems&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;abi-unstability-it-s-much-worse-than-it-seems&quot; tabindex=&quot;-1&quot;&gt;ABI unstability, it’s much worse than it seems &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-start/#abi-unstability-it-s-much-worse-than-it-seems&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I recently had this very late realization about ABI stability in Rust. Up until this point I thought that even though Rust’s ABI is &lt;em&gt;unstable&lt;/em&gt;, one could dynamically load a library safely as long as both the library and the main binary were compiled with the same exact compiler/&lt;code&gt;std&lt;/code&gt;/etc version. I had read this many times in forums like &lt;a href=&quot;https://www.reddit.com/r/rust/comments/6v29z0/plugin_system_with_api/dlx9w7v/&quot;&gt;this one on Reddit&lt;/a&gt; and in blogposts such as &lt;a href=&quot;https://adventures.michaelfbryan.com/posts/plugins-in-rust/&quot;&gt;“Plugins in Rust”&lt;/a&gt;, so I assumed it was true.&lt;/p&gt;
&lt;p&gt;But turns out that the ABI may not only break between compiler versions, but also &lt;em&gt;compiler runs&lt;/em&gt;. &lt;code&gt;rustc&lt;/code&gt; doesn’t guarantee that a layout is going to be the same in different executions. This is proved by &lt;a href=&quot;https://github.com/rust-lang/compiler-team/issues/457&quot;&gt;rust-lang/compiler-team#457&lt;/a&gt;, the new unstable &lt;code&gt;-Z randomize-layout&lt;/code&gt; flag. It’s pretty much self-explanatory: it randomly reorders &lt;code&gt;repr(rust)&lt;/code&gt; layouts for testing purposes. The same thing could happen in the future without an explicit flag; an optimization may cause the &lt;code&gt;repr(rust)&lt;/code&gt; layouts to change between compilation runs. It’s briefly mentioned in &lt;a href=&quot;https://doc.rust-lang.org/reference/type-layout.html#type-layout&quot;&gt;the Rust reference&lt;/a&gt; as well:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Type layout can be changed with each compilation. Instead of trying to document exactly what is done, we only document what is guaranteed today.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Props to the devs at the &lt;code&gt;#black-magic&lt;/code&gt; channel in &lt;a href=&quot;https://discord.com/invite/rust&quot;&gt;Rust’s Discord server&lt;/a&gt;, who helped me understand this. Specially Yandros and Kixiron, both of them very respectable contributors to the Rust compiler/community.&lt;/p&gt;
&lt;p&gt;This basically means that we are &lt;em&gt;forced&lt;/em&gt; to stick to the C ABI with &lt;code&gt;#[repr(C)]&lt;/code&gt;, and that we should use &lt;a href=&quot;https://crates.io/crates/abi_stable&quot;&gt;&lt;code&gt;abi_stable&lt;/code&gt;&lt;/a&gt; in order to have access to a stable &lt;code&gt;std&lt;/code&gt; library as well, instead of re-implementing everything ourselves from scratch. On the positive side, it means that plugins could be implemented in any language, but that wasn’t important for Tremor’s case since the beginning anyway.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_getting_a_simple_example_running&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;getting-a-simple-example-running&quot; tabindex=&quot;-1&quot;&gt;Getting a simple example running &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-start/#getting-a-simple-example-running&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;I’ve created the &lt;a href=&quot;https://github.com/marioortizmanero/pdk-experiments&quot;&gt;pdk-experiments&lt;/a&gt; repository, where I’ll write various examples of how the plugin system might work. The first experiment is in the &lt;a href=&quot;https://github.com/marioortizmanero/pdk-experiments/tree/master/dynamic-simple&quot;&gt;&lt;code&gt;dynamic-simple&lt;/code&gt; directory&lt;/a&gt;, with a small example of how to dynamically load plugins with Rust.&lt;/p&gt;
&lt;p&gt;We first need a crate called &lt;code&gt;common&lt;/code&gt;, which defines the interface shared by the plugin and the runtime in the main binary. In this case it’s just a pointer to a function with the C ABI. We can specify the ABI with &lt;code&gt;extern &amp;quot;C&amp;quot;&lt;/code&gt;, or simply &lt;code&gt;extern&lt;/code&gt;, as &lt;code&gt;&amp;quot;C&amp;quot;&lt;/code&gt; is already its default value&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-start/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;. To keep it simple it’ll just compute the minimum between two integers:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// in common/src/lib.rs&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;MinFunction&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;unsafe&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With it, the plugin crate may export its own implementation. In this case I’ll declare a &lt;code&gt;static&lt;/code&gt; variable, but the example showcases how &lt;code&gt;extern&lt;/code&gt; may work as well. Since we want to use the C ABI, we’ll have to specify &lt;code&gt;crate-type&lt;/code&gt; as &lt;code&gt;cdylib&lt;/code&gt; in our &lt;code&gt;Cargo.toml&lt;/code&gt;. Note that &lt;code&gt;#[no_mangle]&lt;/code&gt; is necessary so that the variable’s name isn’t &lt;a href=&quot;https://en.wikipedia.org/wiki/Name_mangling&quot;&gt;&lt;em&gt;mangled&lt;/em&gt;&lt;/a&gt; and we can access it when dynamically loading the library.&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// in plugin-sample/src/lib.rs&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#[no_mangle]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; with_static&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MinFunction&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; min&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extern&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, the main binary can load the library with &lt;a href=&quot;https://crates.io/crates/libloading&quot;&gt;&lt;code&gt;libloading&lt;/code&gt;&lt;/a&gt;, which requires a bit of &lt;code&gt;unsafe&lt;/code&gt;. I was looking forward to using a different library because of how easy it seems to end up with undefined behaviour in that case. I found out &lt;a href=&quot;https://crates.io/crates/sharedlib&quot;&gt;&lt;code&gt;sharedlib&lt;/code&gt;&lt;/a&gt; was abandoned, as no commits had been made since 2017, leaving &lt;a href=&quot;https://crates.io/crates/dlopen&quot;&gt;&lt;code&gt;dlopen&lt;/code&gt;&lt;/a&gt; as the only alternative. Which was updated two years ago as well, but their GitHub repo seemed somewhat active in comparison.&lt;/p&gt;
&lt;p&gt;For now, I’ll just use &lt;code&gt;libloading&lt;/code&gt; for being the most popular crate, and perhaps I’ll consider using &lt;code&gt;dlopen&lt;/code&gt; in the future. In terms of relevant features and performance they’re pretty close anyway&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-start/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;. Here’s what the code looks like:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// in src/main.rs&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;run_plugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;libloading&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;unsafe&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; library &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Library&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; min &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; library&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MinFunction&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;b&quot;plugin_function&#92;0&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Running plugin:&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;  min(1, 2): {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;  min(-10, 10): {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;  min(2000, 2000): {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can run it with the following commands (though the &lt;code&gt;Makefile&lt;/code&gt; in the repo will do everything for you):&lt;/p&gt;
&lt;pre class=&quot;language-plain&quot;&gt;&lt;code class=&quot;language-plain&quot;&gt;$ cd plugin-sample
$ cargo build --release
$ cd ..
$ cargo run --release -- plugin-sample/target/release/libplugin_sample.so
Running plugin:
  min(1, 2): 1
  min(-10, 10): -10
  min(2000, 2000): 2000&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cool! This raises a few questions that I should learn more about:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Since we’re using the C ABI, is it perhaps best to declare the bindings in C? The &lt;code&gt;common&lt;/code&gt; crate I introduced earlier could just be a header.&lt;/li&gt;
&lt;li&gt;There are many options to configure &lt;code&gt;crate-type&lt;/code&gt; as a &lt;a href=&quot;https://doc.rust-lang.org/reference/linkage.html&quot;&gt;dynamic library&lt;/a&gt;. What are they and which one should I choose?&lt;/li&gt;
&lt;li&gt;I faintly remember that &lt;code&gt;rlib&lt;/code&gt; files are Rust-only objects with additional metadata for things like generics. Could that possibly work at runtime? As in, is there an equivalent to &lt;a href=&quot;https://en.wikipedia.org/wiki/Component_Object_Model&quot;&gt;COM&lt;/a&gt; in Rust, or maybe like JAR files in Java?&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a name=&quot;_generating_bindings&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;generating-bindings&quot; tabindex=&quot;-1&quot;&gt;Generating bindings &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-start/#generating-bindings&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The public interface for the plugins can be written either in Rust (thanks to &lt;code&gt;extern &amp;quot;C&amp;quot;&lt;/code&gt;) or directly in C. There are two commonly used projects when writing bindings:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://rust-lang.github.io/rust-bindgen/&quot;&gt;&lt;code&gt;rust-bindgen&lt;/code&gt;&lt;/a&gt; generates Rust bindings from C code&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/eqrion/cbindgen&quot;&gt;&lt;code&gt;cbindgen&lt;/code&gt;&lt;/a&gt; is the opposite; it generates C bindings from Rust code.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Some examples of its usage:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://crates.io/crates/hyper&quot;&gt;&lt;code&gt;hyper&lt;/code&gt;&lt;/a&gt; is a crate completely written in Rust that exposes C headers for compatibility, so it uses &lt;code&gt;cbindgen&lt;/code&gt; to generate them automatically.&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://crates.io/crates/pipewire_rs&quot;&gt;&lt;code&gt;pipewire_rs&lt;/code&gt;&lt;/a&gt; exposes the interface of &lt;a href=&quot;https://pipewire.org/&quot;&gt;PipeWire&lt;/a&gt;, written in C, so that it’s also available from Rust, thanks to &lt;code&gt;rust-bindgen&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Since we’re going to write the plugin system in Rust, the most appropiate choice would be to use Rust for the interface as well. And if we wanted to make the plugin interface available to other languages — which is not a concern right now — it’d be as “easy” as setting up &lt;code&gt;cbindgen&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_crate_type_values&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;crate-type-values&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;crate-type&lt;/code&gt; values &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-start/#crate-type-values&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There are &lt;a href=&quot;https://doc.rust-lang.org/reference/linkage.html&quot;&gt;two ways to configure dynamic linking with the &lt;code&gt;crate-type&lt;/code&gt; field&lt;/a&gt; in the crate’s &lt;code&gt;Cargo.toml&lt;/code&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;dylib&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cdylib&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Once again, this difference has to do with the ABIs in the dynamic library&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-start/#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;. &lt;code&gt;cdylib&lt;/code&gt; is meant for linking into C/C++ programs (so it strips away all functions that aren’t publicly exported), and &lt;code&gt;dylib&lt;/code&gt; is meant for Rust libraries.&lt;/p&gt;
&lt;p&gt;When compiling the previous example with &lt;code&gt;dylib&lt;/code&gt;, the resulting shared object for the plugin has a size of 4.8Mb, whereas with &lt;code&gt;cdylib&lt;/code&gt; it’s just 2.9Mb. So while both of these will work for our C ABI, &lt;code&gt;cdylib&lt;/code&gt; is clearly the more appropiate choice.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_rlib_files&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;rlib-files&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;rlib&lt;/code&gt; files &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-start/#rlib-files&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;rlib&lt;/code&gt; is another value for &lt;code&gt;crate-type&lt;/code&gt; to generate Rust &lt;strong&gt;static&lt;/strong&gt; libraries, which can then be imported with &lt;code&gt;extern crate crate_name&lt;/code&gt;. But since &lt;code&gt;rlib&lt;/code&gt; files are static libraries, they can’t be loaded at runtime, so they’re of no use in a plugin system.&lt;/p&gt;
&lt;p&gt;Here’s a crazy idea though: What if the &lt;code&gt;rlib&lt;/code&gt; files were dynamically loaded as plugins with the help of &lt;a href=&quot;https://github.com/rust-lang/miri&quot;&gt;MIRI&lt;/a&gt;? I recently learned about it, and quoting its official documentation:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;[MIRI is] an experimental interpreter for Rust’s mid-level intermediate representation (MIR). It can run binaries and test suites of cargo projects and detect certain classes of undefined behavior.&lt;/p&gt;
&lt;p&gt;&lt;mark&gt;You can use Miri to emulate programs on other targets&lt;/mark&gt;, e.g., to ensure that byte-level data manipulation works correctly both on little-endian and big-endian systems.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Hmm. Could it possibly be used to interpret Rust code? In some way this would be very similar to using WebAssembly, but theoretically with less friction, as MIR is specific to Rust and plugin development would be as easy as in the case of dynamic loading with Rust-to-Rust FFI. A few things to consider:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Is this even possible?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;The Rust compiler itself uses MIRI to evaluate constant expressions&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-start/#fn4&quot; id=&quot;fnref4&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt; via the &lt;a href=&quot;https://doc.rust-lang.org/nightly/nightly-rustc/rustc_mir/index.html&quot;&gt;&lt;code&gt;rustc_mir&lt;/code&gt; crate&lt;/a&gt;. But taking a quick look it seems to be created specifically for the compiler, at a very low level, and without that much documentation. Plus, it’s nightly-only. It does seem possible, but I wasn’t able to get a simple example working.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Is MIR stable?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;MIR is unfortunately unstable&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-start/#fn5&quot; id=&quot;fnref5&quot;&gt;[5]&lt;/a&gt;&lt;/sup&gt;, so we’d have the same incompatibility problems between plugins and the main binary.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Is the overhead of MIRI worth it?&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Considering the previous answers, no, but it was cool to consider and learn about :)&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a name=&quot;_can_we_use_webassembly_for_this&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;can-we-use-webassembly-for-this&quot; tabindex=&quot;-1&quot;&gt;Can we use WebAssembly for this? &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-start/#can-we-use-webassembly-for-this&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I also tried to write a simple example of how plugins would work with WebAssembly. They are available in the &lt;a href=&quot;https://github.com/marioortizmanero/pdk-experiments/tree/master/wasmer-simple&quot;&gt;&lt;code&gt;wasmer-simple&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://github.com/marioortizmanero/pdk-experiments/tree/master/wasmtime-simple&quot;&gt;&lt;code&gt;wasmtime-simple&lt;/code&gt;&lt;/a&gt; directories. It took me considerably more effort to understand and get running than with dynamic linking, even following &lt;a href=&quot;https://freemasen.com/blog/wasmer-plugin-pt-1/&quot;&gt;Free Masen’s guide&lt;/a&gt; and &lt;a href=&quot;https://docs.rs/wasmer&quot;&gt;Wasmer’s official documentation&lt;/a&gt;. But at least I didn’t have to write that much &lt;code&gt;unsafe&lt;/code&gt; (it’s still needed to load or store data from Wasm’s virtual memory, which I’ll explain later on).&lt;/p&gt;
&lt;p&gt;The following snippet is what the plugin would look like. Note that this time we use &lt;code&gt;pub&lt;/code&gt; without even considering &lt;code&gt;static&lt;/code&gt; to export a pointer to the function. Wasm does have support for globals, but since handling complex types — those other than numbers, a function in this case — isn’t trivial, it’s not worth it.&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// in plugin-sample/src/lib.rs&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#[no_mangle]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;with_extern&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;a&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; b&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    a&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;b&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For the runtime, since it’s a simple example I’ll try both &lt;a href=&quot;https://wasmer.io/&quot;&gt;Wasmer&lt;/a&gt; and &lt;a href=&quot;https://wasmtime.dev/&quot;&gt;Wasmtime&lt;/a&gt;. For more complex things I’ll just use Wasmer, since in an &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/&quot;&gt;earlier post&lt;/a&gt; it seemed like the best alternative. I also wanted to try &lt;a href=&quot;https://github.com/alec-deason/wasm_plugin&quot;&gt;&lt;code&gt;wasm_plugin&lt;/code&gt;&lt;/a&gt;, which includes procedural macros to help reduce the overall boilerplate (which will be more important later on), but it seems to be abandoned since 2019. It’s interesting to try the lower-level interface of the runtimes myself to learn more about it anyway, so I’ll just use that for now. If we ended up using Wasm for Tremor I’d try updating and maintaining &lt;code&gt;wasm_plugin&lt;/code&gt; to keep the code boilerplate-free.&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// in src/main.rs (wasmer)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;run_plugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Box&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;dyn&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// For reference, Feather also reads the plugins with `fs::read`:&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// https://github.com/feather-rs/feather/blob/07c64678f80ff77be3dbd3d99fbe5558b4e72c97/quill/cargo-quill/src/main.rs#L107&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; module_wat &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;fs&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; store &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Store&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; module &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Module&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;store&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;module_wat&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; import_object &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token macro property&quot;&gt;imports!&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; instance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Instance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;module&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;import_object&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Running plugin:&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; min_extern&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;NativeFunc&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; instance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;exports&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_native_function&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;with_extern&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;  min(1, 2): {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; min_extern&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;  min(-10, 10): {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; min_extern&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;  min(2000, 2000): {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; min_extern&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;// in src/main.rs (wasmtime)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;run_plugin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Box&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;dyn&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; engine &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Engine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; module &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Module&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;from_file&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;engine&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;path&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; store &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Store&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;engine&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;WasmState&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; imports &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; instance &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Instance&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; store&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;module&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;imports&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Running plugin:&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; min_extern &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; instance&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;get_typed_func&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; _&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; store&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;with_extern&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;  min(1, 2): {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; min_extern&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; store&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;  min(-10, 10): {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; min_extern&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; store&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;  min(2000, 2000): {}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; min_extern&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;mut&lt;/span&gt; store&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2000&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token class-name&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;For a simple example they’re pretty much the same. Running them:&lt;/p&gt;
&lt;pre class=&quot;language-plain&quot;&gt;&lt;code class=&quot;language-plain&quot;&gt;$ rustup target add wasm32-wasi
$ cd plugin-simple
$ cargo build --target wasm32-wasi --release
$ cd ..
$ cargo run --release -- plugin-sample/target/wasm32-wasi/release/plugin_sample.wasm
Running plugin:
  min(1, 2): 1
  min(-10, 10): -10
  min(2000, 2000): 2000&lt;/code&gt;&lt;/pre&gt;
&lt;ul&gt;
&lt;li&gt;AFAIK Rust has multiple compilation targets for Wasm. Which one should I be using?&lt;/li&gt;
&lt;li&gt;What’s the difference between the &lt;a href=&quot;https://docs.rs/wasmer/&quot;&gt;&lt;code&gt;wasmer&lt;/code&gt;&lt;/a&gt; crate and &lt;a href=&quot;https://docs.rs/wasmer-runtime/&quot;&gt;&lt;code&gt;wasmer_runtime&lt;/code&gt;&lt;/a&gt;?&lt;/li&gt;
&lt;li&gt;What about exporting types more complex than an &lt;code&gt;i32&lt;/code&gt;? Is it possible to export a struct that implements a specific trait?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a name=&quot;_webassembly_targets&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;webassembly-targets&quot; tabindex=&quot;-1&quot;&gt;WebAssembly targets &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-start/#webassembly-targets&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Wasmer docs don’t mention this much because targets are related to the plugin, rather than the runtime. But Wasmtime’s book does include &lt;a href=&quot;https://docs.wasmtime.dev/wasm-rust.html&quot;&gt;a section&lt;/a&gt; about compiling Rust to WebAssembly:&lt;/p&gt;
&lt;blockquote&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;wasm32-wasi&lt;/code&gt; - when using wasmtime this is likely what you’ll be using. The WASI target is integrated into the standard library and is intended on producing standalone binaries.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wasm32-unknown-unknown&lt;/code&gt; - this target, like the WASI one, is focused on producing single *.wasm binaries. The standard library, however, is largely stubbed out since the “unknown” part of the target means libstd can’t assume anything. This means that while binaries will likely work in wasmtime, common conveniences like println! or panic! won’t work.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;wasm32-unknown-emscripten&lt;/code&gt; - this target is intended to work in a web browser and produces a *.wasm file coupled with a *.js file, and it is not compatible with wasmtime.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;
&lt;p&gt;So basically what we need is &lt;code&gt;wasm32-wasi&lt;/code&gt;. By the way, even though Wasmer was my initial choice, to be honest I found Wasmtime’s docs to be much more detailed and well-organized.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_wasmer_vs_wasmer_runtime&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;wasmer-vs-wasmer-runtime&quot; tabindex=&quot;-1&quot;&gt;&lt;code&gt;wasmer&lt;/code&gt; vs &lt;code&gt;wasmer_runtime&lt;/code&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-start/#wasmer-vs-wasmer-runtime&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This was confusing for me at first, since both crates seem to have a very similar interface and almost the same set of authors. Some tutorials used &lt;code&gt;wasmer&lt;/code&gt;, others &lt;code&gt;wasmer_runtime&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The difference seems to be that &lt;code&gt;wasmer_runtime&lt;/code&gt; was updated about a year ago, while &lt;code&gt;wasmer&lt;/code&gt; got bumped to v2.0.0 just two months ago. The last release of &lt;code&gt;wasmer_runtime&lt;/code&gt; is v0.17 (v0.18 seems to be yanked), and the first one of &lt;code&gt;wasmer&lt;/code&gt; is v0.17 as well, so my bet is that &lt;code&gt;wasmer_runtime&lt;/code&gt; is the name of the crate their team used previously, and they eventually deprecated it in favor of &lt;code&gt;wasmer&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I’ve opened an &lt;a href=&quot;https://github.com/wasmerio/wasmer/issues/2539&quot;&gt;issue&lt;/a&gt; upstream so that this is hopefully more clear to future users.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_more_complex_types&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;more-complex-types&quot; tabindex=&quot;-1&quot;&gt;More complex types &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-start/#more-complex-types&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The WebAssembly specification only defines integers and floating point as its supported types&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-start/#fn6&quot; id=&quot;fnref6&quot;&gt;[6]&lt;/a&gt;&lt;/sup&gt;. There are three ways to handle non-trivial types like structs or enums:&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_the_interface_types_proposal&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;option-1-the-interface-types-proposal&quot; tabindex=&quot;-1&quot;&gt;Option 1: The Interface Types Proposal &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-start/#option-1-the-interface-types-proposal&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/webassembly/interface-types&quot;&gt;This proposal for WebAssembly&lt;/a&gt; defines the binary format for encoding and decoding the newly supported types, and specifies a set of instructions to transform the data between WebAssembly and the outside world. Note that this proposal is not meant to define a fixed representation of e.g., a string in Wasm, it attempts to allow representation-agnostic high-level value types.&lt;/p&gt;
&lt;p&gt;These new &lt;em&gt;high-level value types&lt;/em&gt; are called &lt;strong&gt;interface types&lt;/strong&gt;. The current proposal defines them as:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Floating point of 32 and 64 bits&lt;/li&gt;
&lt;li&gt;Signed and unsigned integers of up to 64 bits&lt;/li&gt;
&lt;li&gt;Single characters&lt;/li&gt;
&lt;li&gt;Lists (a string would be a list of characters)&lt;/li&gt;
&lt;li&gt;Records (basically like Rust structs)&lt;/li&gt;
&lt;li&gt;Variants (basically like Rust enums or unions, it would enable &lt;code&gt;Option&lt;/code&gt;, &lt;code&gt;Result&lt;/code&gt;, etc)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Additionally, language-neutral interfaces for WASI can be defined with the experimental &lt;em&gt;&lt;code&gt;witx&lt;/code&gt; files&lt;/em&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-start/#fn7&quot; id=&quot;fnref7&quot;&gt;[7]&lt;/a&gt;&lt;/sup&gt;, which makes it easy to define a common ABI in order to interact from different programming languages&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-start/#fn8&quot; id=&quot;fnref8&quot;&gt;[8]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-start/#fn9&quot; id=&quot;fnref9&quot;&gt;[9]&lt;/a&gt;&lt;/sup&gt;. They look like this:&lt;/p&gt;
&lt;pre class=&quot;language-lisp&quot;&gt;&lt;code class=&quot;language-lisp&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token car&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;errno.witx&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token heading comment title&quot;&gt;;;; Add two integers&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token car&quot;&gt;module&lt;/span&gt; $calculator
  &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token car&quot;&gt;@interface&lt;/span&gt; func &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token car&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;add&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token car&quot;&gt;param&lt;/span&gt; $lh s32&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token car&quot;&gt;param&lt;/span&gt; $rh s32&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token car&quot;&gt;result&lt;/span&gt; $error $errno&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token car&quot;&gt;result&lt;/span&gt; $res s32&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Please refer to the &lt;a href=&quot;https://github.com/WebAssembly/interface-types/blob/master/proposals/interface-types/Explainer.md&quot;&gt;proposal itself&lt;/a&gt; for more detailed information; it’s very well explained.&lt;/p&gt;
&lt;p&gt;The main problem is that this proposal is still at &lt;a href=&quot;https://github.com/WebAssembly/proposals#phase-1---feature-proposal-cg&quot;&gt;Phase 1&lt;/a&gt;. It’s still actively being worked on, and its specification is far from stable.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;At the plugin level the &lt;a href=&quot;https://crates.io/crates/wasm_bindgen&quot;&gt;&lt;code&gt;wasm_bindgen&lt;/code&gt;&lt;/a&gt; crate seems to be ideal, and it already works well. It’s a very simple procedural macro that can be added to the exported functions in the plugin in order to automatically add support for Interface Types.&lt;/li&gt;
&lt;li&gt;The &lt;a href=&quot;https://crates.io/crates/wiggle&quot;&gt;&lt;code&gt;wiggle&lt;/code&gt;&lt;/a&gt; crate can be used to generate Rust code from &lt;code&gt;witx&lt;/code&gt; files. For the previous snippet of code, wiggle’s macro will generate a &lt;code&gt;Calculator&lt;/code&gt; trait and the defined types in another module named &lt;code&gt;types&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The runtimes, however, don’t work with Interface types:
&lt;ul&gt;
&lt;li&gt;Wasmtime did support this in the past until their implementation was removed after being considered outdated. As &lt;a href=&quot;https://github.com/bytecodealliance/wasmtime/issues/677&quot;&gt;this issue indicates&lt;/a&gt;, it still hasn’t been updated.&lt;/li&gt;
&lt;li&gt;Wasmer has the &lt;a href=&quot;https://docs.rs/wasmer-interface-types/&quot;&gt;&lt;code&gt;wasmer_interface_types&lt;/code&gt;&lt;/a&gt; crate, but with a similar story; it’s outdated. There’s &lt;a href=&quot;https://github.com/wasmerio/wasmer/issues/2480&quot;&gt;this issue&lt;/a&gt; as a continuation of Wasmtime’s, which explains the situation.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;In the end I wasn’t able to get Interface Types working, nor I considered them worth my time, as it’s still too early.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_the_hacky_but_working_way&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;option-2-the-hacky-but-working-way&quot; tabindex=&quot;-1&quot;&gt;Option 2: The hacky-but-working way &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-start/#option-2-the-hacky-but-working-way&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Via pointers and a shared &lt;a href=&quot;https://docs.wasmer.io/integrations/examples/memory&quot;&gt;memory&lt;/a&gt;. The user has to first construct and serialize the complex types, and then save them into Wasm’s memory, which can be accessed directly by the runtime or the plugin with &lt;a href=&quot;https://docs.wasmer.io/integrations/examples/memory-pointers&quot;&gt;pointers&lt;/a&gt;. This is what &lt;a href=&quot;https://github.com/feather-rs/feather/tree/main/quill&quot;&gt;Feather&lt;/a&gt; or &lt;a href=&quot;https://book.veloren.net/contributors/developers/codebase-structure.html#plugins&quot;&gt;Veloren&lt;/a&gt; do, in case you want more details.&lt;/p&gt;
&lt;p&gt;Not only does this require a serialization and deserialization step and writing/reading all the data from memory, but also it’s very cumbersome to use and easy to mess up. It’s somewhat trivial though, so a procedural macro like the now outdated &lt;a href=&quot;https://crates.io/crates/wasmer-plugin&quot;&gt;&lt;code&gt;wasmer-plugin&lt;/code&gt;&lt;/a&gt; could simplify it. For now, &lt;a href=&quot;https://crates.io/crates/bincode&quot;&gt;&lt;code&gt;bincode&lt;/code&gt;&lt;/a&gt; can be used for the serialization steps manually.&lt;/p&gt;
&lt;p&gt;I did try this and got it working by following &lt;a href=&quot;https://freemasen.com/blog/wasmer-plugin-pt-1/&quot;&gt;Free Masen’s guide&lt;/a&gt;, but it wasn’t worth exploring in depth because it requires a [de]serialization step Tremor can’t afford.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_separate_executables&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;option-3-separate-executables&quot; tabindex=&quot;-1&quot;&gt;Option 3: Separate executables &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-start/#option-3-separate-executables&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Another way I’ve seen to work around this is by using Wasm as an executable instead of a library. &lt;a href=&quot;https://github.com/zellij-org/zellij&quot;&gt;Zellij&lt;/a&gt; does this, and communicates with the plugins via the standard input and standard output streams.&lt;/p&gt;
&lt;p&gt;Unfortunately, this also requires [de]serializing and has the overhead of writing/reading all the data from stdin and stdout.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_conclusion&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-start/#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Ultimately, this has forced me to discard WebAssembly for Tremor’s Plugin Development Kit. Performance is really important in this specific case, and Wasm doesn’t offer a solution to efficiently pass data between the host and the plugin. For most use cases Wasm still may be of interest, so hopefully I’ve included enough links and ideas so that you can get started if it’s the way you want to go.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_wrapping_up&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;wrapping-up&quot; tabindex=&quot;-1&quot;&gt;Wrapping up &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-start/#wrapping-up&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Today we’ve learned a lot of things both about dynamic loading and WebAssembly. Unfortunately, I’ve had to discard the latter for Tremor’s case, but it may be of interest to projects that aren’t so focused on performance. Or maybe by the time you, the reader, have finished this article, the issues I found are already stabilized and broadly used, which would make Wasm an even more appealing choice.&lt;/p&gt;
&lt;p&gt;In the next post, I will try to get a more advanced example of a plugin system with dynamic loading. The goal is to simulate the end-product by writing it as close as possible to how it would be used in real life. This way, we will also be able to benchmark it against a statically linked implementation and analyze the performance overhead we’ll end up getting.&lt;/p&gt;
&lt;p&gt;I did write a few benchmarks for the examples provided in this article, but they aren’t fair at all nor representative of a real-life situation. They simply load the plugin and run the &lt;code&gt;min&lt;/code&gt; function once, or in the case of dynamic loading twice (one with &lt;code&gt;static&lt;/code&gt; and another with &lt;code&gt;extern&lt;/code&gt;). And since they’re very simple examples, the Wasm ones don’t include the [de]serialization part that may introduce even more overhead. Knowing that, the results are the following, which is more or less what I was expecting:&lt;/p&gt;
&lt;pre class=&quot;language-plain&quot;&gt;&lt;code class=&quot;language-plain&quot;&gt;test test::dynamic_simple ... bench:     139,702 ns/iter (+/- 34,699)
test test::wasmer_setup   ... bench:     967,633 ns/iter (+/- 203,933)
test test::wasmtime_setup ... bench:     988,500 ns/iter (+/- 363,244)&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I hope you enjoyed reading this post and that you learned something from it! You can leave any suggestions in the comments below.&lt;/p&gt;

&lt;script src=&quot;https://f.convertkit.com/ckjs/ck.5.js&quot;&gt;&lt;/script&gt;
&lt;form action=&quot;https://app.kit.com/forms/7534343/subscriptions&quot; class=&quot;seva-form formkit-form&quot; method=&quot;post&quot; data-sv-form=&quot;7534343&quot; data-uid=&quot;3680381778&quot; data-format=&quot;inline&quot; data-version=&quot;5&quot; data-options=&#39;{&quot;settings&quot;:{&quot;after_subscribe&quot;:{&quot;action&quot;:&quot;message&quot;,&quot;success_message&quot;:&quot;Success! Check your email to confirm your subscription. If you can’t find it, look in the spam folder.&quot;,&quot;redirect_url&quot;:&quot;&quot;},&quot;analytics&quot;:{&quot;google&quot;:null,&quot;fathom&quot;:null,&quot;facebook&quot;:null,&quot;segment&quot;:null,&quot;pinterest&quot;:null,&quot;sparkloop&quot;:null,&quot;googletagmanager&quot;:null},&quot;modal&quot;:{&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;powered_by&quot;:{&quot;show&quot;:true,&quot;url&quot;:&quot;https://kit.com/features/forms?utm_campaign=poweredby&amp;amp;utm_content=form&amp;amp;utm_medium=referral&amp;amp;utm_source=dynamic&quot;},&quot;recaptcha&quot;:{&quot;enabled&quot;:false},&quot;return_visitor&quot;:{&quot;action&quot;:&quot;show&quot;,&quot;custom_content&quot;:&quot;&quot;},&quot;slide_in&quot;:{&quot;display_in&quot;:&quot;bottom_right&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;sticky_bar&quot;:{&quot;display_in&quot;:&quot;top&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15}},&quot;version&quot;:&quot;5&quot;}&#39; min-width=&quot;400 500 600 700 800&quot;&gt;
  &lt;div data-style=&quot;clean&quot;&gt;
    &lt;div class=&quot;formkit-header&quot; data-element=&quot;header&quot;&gt;
      &lt;h2&gt;Want to receive an email when I post?&lt;/h2&gt;
    &lt;/div&gt;
    &lt;ul class=&quot;formkit-alert formkit-alert-error&quot; data-element=&quot;errors&quot; data-group=&quot;alert&quot;&gt;&lt;/ul&gt;
    &lt;div data-element=&quot;fields&quot; data-stacked=&quot;false&quot; class=&quot;seva-fields formkit-fields&quot;&gt;
      &lt;div class=&quot;formkit-field&quot;&gt;
        &lt;input class=&quot;formkit-input&quot; name=&quot;email_address&quot; aria-label=&quot;Email Address&quot; placeholder=&quot;Email Address&quot; required=&quot;&quot; type=&quot;email&quot;&gt;
      &lt;/div&gt;
      &lt;button data-element=&quot;submit&quot; class=&quot;formkit-submit&quot; style=&quot;background-color:#e06c75;font-weight:700&quot;&gt;
        &lt;div class=&quot;formkit-spinner&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span class=&quot;&quot;&gt;Subscribe&lt;/span&gt;
      &lt;/button&gt;
    &lt;/div&gt;
    &lt;div class=&quot;formkit-guarantee&quot; data-element=&quot;guarantee&quot;&gt;
      &lt;p&gt;&lt;em&gt;Free and without spam. You can also use my &lt;a href=&quot;https://nullderef.com/index.xml&quot; target=&quot;_blank&quot;&gt;RSS&lt;/a&gt;, &lt;a href=&quot;https://x.com/null_deref&quot; target=&quot;_blank&quot;&gt;X&lt;/a&gt;, or &lt;a href=&quot;https://mstdn.social/@nullderef&quot; target=&quot;_blank&quot;&gt;Mastodon&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
      &lt;p style=&quot;opacity:.6;font-size:16px&quot;&gt;&lt;em&gt;Powered by &lt;a href=&quot;https://kit.com/&quot; target=&quot;_black&quot;&gt;Kit&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;style&gt;.formkit-form[data-uid=&quot;3680381778&quot;] a{text-decoration:underline}.formkit-form[data-uid=&quot;3680381778&quot;] legend{border:none;font-size:inherit;margin-bottom:10px;padding:0;position:relative;display:table}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-checkboxes,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-select{width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{border:0;border-radius:var(--radius);color:#fff;cursor:pointer;display:inline-block;text-align:center;font-weight:500;cursor:pointer;margin-bottom:15px;overflow:hidden;padding:0;position:relative;vertical-align:middle}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover{outline:0}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover&gt;span{background-color:rgba(0,0,0,.1)}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit&gt;span{display:block;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out;padding:12px 24px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input{background:#fff;padding:12px;border:1px solid var(--border);border-radius:var(--radius);-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;line-height:1.4;margin:0;-webkit-transition:border-color ease-out .3s;transition:border-color ease-out .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:focus{outline:0;border-color:#1677be;-webkit-transition:border-color ease .3s;transition:border-color ease .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-webkit-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-moz-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:-ms-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert{background:#f9fafb;border:1px solid #e3e3e3;border-radius:5px;-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;list-style:none;margin:25px auto;padding:12px;text-align:center;width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert:empty{display:none}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-success{background:#d3fbeb;border-color:#10bf7a;color:#0c905c}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-error{background:#fde8e2;border-color:#f2643b;color:#ea4110}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;height:0;width:0;margin:0 auto;position:absolute;top:0;left:0;right:0;width:0;overflow:hidden;text-align:center;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div{margin:auto;width:12px;height:12px;background-color:#fff;opacity:.3;border-radius:100%;display:inline-block;-webkit-animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both;animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:first-child{-webkit-animation-delay:-.32s;animation-delay:-.32s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:nth-child(2){-webkit-animation-delay:-.16s;animation-delay:-.16s}.formkit-form[data-uid=&quot;3680381778&quot;]{border:1px solid var(--border);border-radius:var(--radius);position:relative;overflow:hidden;background:var(--code-bg)}.formkit-form[data-uid=&quot;3680381778&quot;]{padding:20px;width:100%;position:relative}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-header{margin:0 0 27px 0;font-size:20px;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee{margin-top:10px;margin-bottom:32px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee&gt;p{margin:0;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-fields{display:flex;flex-wrap:wrap;margin:25px auto 0 auto;gap:10px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-field,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{flex:1 0 100%}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;]{padding:0 30px}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-field{flex:100 1 auto}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-submit{flex:1 1 auto}&lt;/style&gt;
&lt;/form&gt;
&lt;h2 id=&quot;footnotes&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://doc.rust-lang.org/std/keyword.extern.html&quot;&gt;Keyword extern — doc.rust-lang.org&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-start/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/szymonwieloch/rust-dlopen#compare-with-other-libraries&quot;&gt;Compare with other libraries —  GitHub szymonwieloch/rust-dlopen&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-start/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://users.rust-lang.org/t/what-is-the-difference-between-dylib-and-cdylib/28847&quot;&gt;What is the Difference Between &lt;code&gt;dylib&lt;/code&gt; and &lt;code&gt;cdylib&lt;/code&gt; — users.rust-lang.org&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-start/#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn4&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://rustc-dev-guide.rust-lang.org/miri.html&quot;&gt;Miri — Guide to Rustc Development&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-start/#fnref4&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn5&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/rust-lang/miri/blob/master/CONTRIBUTING.md#preparing-the-build-environment&quot;&gt;Preparing the build environment — Github rust-lang/miri&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-start/#fnref5&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn6&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://docs.rs/wasmer-runtime-core/0.17.1/wasmer_runtime_core/types/trait.WasmExternType.html&quot;&gt;&lt;code&gt;WasmExternType&lt;/code&gt; — Wasmer v0.17.1&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-start/#fnref6&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn7&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/WebAssembly/WASI/blob/4ab96a8e051f6c0c921549b5bf7dfa47cccef1ed/docs/witx.md&quot;&gt;Know your &lt;code&gt;witx&lt;/code&gt; — GitHub WebAssembly/WASI&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-start/#fnref7&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn8&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/WebAssembly/interface-types/blob/master/proposals/interface-types/Explainer.md#defining-language-neutral-interfaces-like-wasi-revisited&quot;&gt;Defining language-neutral interfaces like WASI (revisited) — GitHub WebAssembly/interface-types&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-start/#fnref8&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn9&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://radu-matei.com/blog/wasm-api-witx/&quot;&gt;Writing a simple WASM API layer using interface types and Wasmtime — radu’s blog&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-start/#fnref9&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</content>
  </entry>
  <entry>
    <title>Why you shouldn&#39;t obsess about Rust &quot;features&quot;</title>
    <link href="https://nullderef.com/blog/rust-features/" />
    <updated>2021-07-06T00:00:00Z</updated>
    <id>https://nullderef.com/blog/rust-features/</id>
    <content type="html">&lt;details class=&quot;toc&quot;&gt;
&lt;summary&gt;Table of Contents&lt;/summary&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-features/#the-problem&quot;&gt;The Problem &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-features/#an-alternative&quot;&gt;An Alternative &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-features/#proving-that-you-end-up-with-the-same-code&quot;&gt;Proving that you end up with the same code &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-features/#you-can-probably-afford-the-overhead-anyway&quot;&gt;You can probably afford the overhead anyway &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-features/#conclusion&quot;&gt;Conclusion &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/details&gt;
&lt;p&gt;Rust makes it very easy to express conditional compilation, especially thanks to Cargo &lt;a href=&quot;https://doc.rust-lang.org/cargo/reference/features.html&quot;&gt;“features”&lt;/a&gt;. They’re well integrated into the language and are very easy to use. But one thing I’ve learned by maintaining &lt;a href=&quot;https://github.com/ramsayleung/rspotify&quot;&gt;RSpotify&lt;/a&gt; (a library for the Spotify API) is that one shouldn’t obsess over them. Conditional compilation should be used when it’s &lt;em&gt;the only way&lt;/em&gt; to solve the problem, for a number of reasons I’ll explain in this article.&lt;/p&gt;
&lt;p&gt;This might be obvious, but to me, it wasn’t so clear back when I started using Rust. Even if you’re already aware, it might be a good reminder — maybe you forgot about it in your latest project, and you added an unnecessary feature.&lt;/p&gt;
&lt;p&gt;Conditional compilation isn’t anything new; C and C++ have been doing it for a long time now. So this principle can also be applied in these cases. However, in my experience, it’s much easier to work with conditional compilation in Rust, meaning that it’s more likely to be misused.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_the_problem&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-problem&quot; tabindex=&quot;-1&quot;&gt;The Problem &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-features/#the-problem&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I went through this dilemma when deciding how to configure cached tokens in &lt;a href=&quot;https://github.com/ramsayleung/rspotify&quot;&gt;RSpotify&lt;/a&gt;. Said library gives you the possibility of persistently managing the authentication token via a JSON file. That way, when the program is launched again, the token from the previous session can be reused. One doesn’t have to follow the full auth process again — that is, until the token expires.&lt;/p&gt;
&lt;p&gt;Originally, this was going to be a compile-time feature named &lt;code&gt;cached_token&lt;/code&gt;. I didn’t give it much thought; why would one need the code to save and read the token file if you just don’t need it? The easiest way to do that is using a feature you can just toggle in your &lt;code&gt;Cargo.toml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I later worked on another very similar feature, &lt;code&gt;refreshing_token&lt;/code&gt;. When optionally enabled, the client would automatically refresh expired tokens. As this pattern appeared more and more in the library, I wanted to make sure its design was optimal. Once I took a deeper look, I began to find the many inconveniences of Cargo features:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;They’re &lt;strong&gt;inflexible&lt;/strong&gt;: you can’t have a client with cached tokens and another without them in the same program. It’s a library-wide thing, so you either enable them or you don’t. Obviously, they’re not configurable at runtime either; a user might want to choose what kind of behaviour to follow &lt;em&gt;while&lt;/em&gt; the program is running.&lt;/li&gt;
&lt;li&gt;They’re &lt;strong&gt;ugly&lt;/strong&gt;: writing &lt;code&gt;#[cfg(feature = &amp;quot;cached_token&amp;quot;)]&lt;/code&gt; is much more weird and verbose than a plain &lt;code&gt;if cached_token&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;They’re &lt;strong&gt;messy&lt;/strong&gt;: features are hard to manage in the codebase. You can find yourself in the Rust equivalent of an &lt;a href=&quot;https://www.cqse.eu/en/news/blog/living-in-the-ifdef-hell/&quot;&gt;&lt;code&gt;#ifdef&lt;/code&gt; hell&lt;/a&gt; very easily.&lt;/li&gt;
&lt;li&gt;They’re &lt;strong&gt;hard to document and test&lt;/strong&gt;: Rust doesn’t provide a way to expose the features of a library. All you can do is list them manually in the main page of the docs. Testing is also harder because you have to figure out what combinations of features to use in order to cover the entire codebase, and apply them whenever you want to run the tests.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;All of these are supposedly outweighed by just being guaranteed that the binary won’t have code you don’t need. But how true is that, really? And how important is it?&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_an_alternative&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;an-alternative&quot; tabindex=&quot;-1&quot;&gt;An Alternative &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-features/#an-alternative&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Turns out that one of the simplest optimizations a compiler can implement is the propagation of constants. This, in combination with the removal of dead code, can result in exactly the same effect as features, but in a more natural way. Instead of adding features to configure the behaviour of your program, you can do the same with a &lt;code&gt;Config&lt;/code&gt; struct. You may not even need a struct if it’s just a single option to be configured, but that way it’s future-proof. For example:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[derive(Default)]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Config&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    cached_token&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    refreshing_token&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can then modify your client in order to optionally take the &lt;code&gt;Config&lt;/code&gt; struct:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Client&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    config&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Config&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Client&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// Uses the default configuration for the initialization&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Client&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Client&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            config&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;/// Uses a custom configuration for the initialization&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;with_config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Client&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Client&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            config&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;do_request&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;cached_token &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Saving cache token to the file!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// The previous block used to be equivalent to:&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;//&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// #[cfg(feature = &quot;cached_token&quot;)]&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// {&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;//     println!(&quot;Saving cache token to the file!&quot;);&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// }&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;refreshing_token &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Refreshing token!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Performing request!&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Finally, the user can customize the client however they want in the code itself in a very natural way:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Option A&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;// Option B&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; config &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Config&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        cached_token&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; client &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Client&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;with_config&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;config&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;a name=&quot;_proving_that_you_end_up_with_the_same_code&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;proving-that-you-end-up-with-the-same-code&quot; tabindex=&quot;-1&quot;&gt;Proving that you end up with the same code &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-features/#proving-that-you-end-up-with-the-same-code&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Thanks to the awesome &lt;a href=&quot;https://godbolt.org&quot;&gt;Compiler Explorer&lt;/a&gt;, we can make sure this compiles as we expect it to with &lt;a href=&quot;https://godbolt.org/z/Kr9GP6Gqz&quot;&gt;this snippet&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/blog/rust-features/XuSVtGqvgn-1116.avif 1116w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/blog/rust-features/XuSVtGqvgn-1116.webp 1116w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://nullderef.com/blog/rust-features/XuSVtGqvgn-1116.png&quot; alt=&quot;Assembly comparison&quot; width=&quot;1116&quot; height=&quot;750&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;It seems that as of Rust 1.53, for values of &lt;code&gt;opt-level&lt;/code&gt; greater or equal than 2, the code for the deactivated features doesn’t even appear in the assembly (it’s easy to see by taking a look at the strings at the end). &lt;code&gt;cargo build --release&lt;/code&gt; configures &lt;code&gt;opt-level&lt;/code&gt; to 3&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-features/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;, so it shouldn’t be a problem for production binaries.&lt;/p&gt;
&lt;p&gt;And we aren’t even using &lt;code&gt;const&lt;/code&gt;! I wonder what will happen in that case. With &lt;a href=&quot;https://godbolt.org/z/f1xTaWzdc&quot;&gt;this slightly modified snippet&lt;/a&gt;:&lt;/p&gt;
&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/blog/rust-features/F_h1nZlUpE-1115.avif 1115w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/blog/rust-features/F_h1nZlUpE-1115.webp 1115w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://nullderef.com/blog/rust-features/F_h1nZlUpE-1115.png&quot; alt=&quot;Assembly comparison&quot; width=&quot;1115&quot; height=&quot;687&quot;&gt;&lt;/picture&gt;&lt;/p&gt;
&lt;p&gt;Hmm. We actually get the same results. The generated assembly is exactly the same, and the optional code is optimized away only starting at &lt;code&gt;opt-level=2&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The thing is that &lt;code&gt;const&lt;/code&gt; just means that its value &lt;em&gt;may&lt;/em&gt; (and not must) be inlined&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-features/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-features/#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;. Nothing else. So we still don’t have anything guaranteed, and inlining isn’t enough to simplify the code &lt;em&gt;inside the function&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;So for what I’ve investigated it seems to be best to just not worry about it and use a variable instead of &lt;code&gt;const&lt;/code&gt;. It looks better and gets the same results.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_you_can_probably_afford_the_overhead_anyway&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;you-can-probably-afford-the-overhead-anyway&quot; tabindex=&quot;-1&quot;&gt;You can probably afford the overhead anyway &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-features/#you-can-probably-afford-the-overhead-anyway&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Even if the previous optimization wasn’t implemented, would the optional code cause any harm in the final binary, really? Are we overengineering the solution, as always? Truth is, the optional code for cached/refreshing tokens isn’t even that much bloat.&lt;/p&gt;
&lt;p&gt;It depends, of course, but binary bloat isn’t that much of a problem for higher level binaries, in my opinion. Rust already statically embeds its standard library, its runtime, and a ton of debug info in each binary, which sums up to around 3 MB. And the only overhead you may get at runtime is a branch.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_conclusion&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-features/#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Sometimes you just &lt;em&gt;have&lt;/em&gt; to use conditional compilation; there’s no way around it. You might be dealing with platform-specific code or want to reduce the number of dependencies of your crate, in which cases features are super helpful.&lt;/p&gt;
&lt;p&gt;But that wasn’t RSpotify’s case; conditional compilation was definitely not the way to go. When you’re about to introduce a new feature to your crate, think to yourself, “Do I really need conditional compilation for this?”.&lt;/p&gt;
&lt;p&gt;Neither &lt;code&gt;cached_token&lt;/code&gt; nor &lt;code&gt;refreshing_token&lt;/code&gt; follow the usual reasoning as to why a feature might be added. They don’t give access to new functions/modules. They don’t help get rid of optional dependencies. And they certainly aren’t platform-specific features. They just configure the behaviour of the library.&lt;/p&gt;
&lt;p&gt;In order to avoid this, perhaps the naming for features could have been different? Enabling support for cached tokens certainly sounds like a “feature”, while OS-specific code doesn’t really seem like one. I also find it confusing sometimes, and Google agrees with me in this one. Looking for information related to Rust features might return completely unrelated stuff just because the result has the word “feature” but meaning “an attribute or aspect of the program”. Kind of like how you have to google “golang X” instead of “go X” because otherwise it doesn’t make sense. But whatever, it’s too late for my opinion anyway.&lt;/p&gt;
&lt;p&gt;Anyhow, I hope you learned something new, or that this was at least a good reminder! If you have any suggestions please leave them in the section below :)&lt;/p&gt;

&lt;script src=&quot;https://f.convertkit.com/ckjs/ck.5.js&quot;&gt;&lt;/script&gt;
&lt;form action=&quot;https://app.kit.com/forms/7534343/subscriptions&quot; class=&quot;seva-form formkit-form&quot; method=&quot;post&quot; data-sv-form=&quot;7534343&quot; data-uid=&quot;3680381778&quot; data-format=&quot;inline&quot; data-version=&quot;5&quot; data-options=&#39;{&quot;settings&quot;:{&quot;after_subscribe&quot;:{&quot;action&quot;:&quot;message&quot;,&quot;success_message&quot;:&quot;Success! Check your email to confirm your subscription. If you can’t find it, look in the spam folder.&quot;,&quot;redirect_url&quot;:&quot;&quot;},&quot;analytics&quot;:{&quot;google&quot;:null,&quot;fathom&quot;:null,&quot;facebook&quot;:null,&quot;segment&quot;:null,&quot;pinterest&quot;:null,&quot;sparkloop&quot;:null,&quot;googletagmanager&quot;:null},&quot;modal&quot;:{&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;powered_by&quot;:{&quot;show&quot;:true,&quot;url&quot;:&quot;https://kit.com/features/forms?utm_campaign=poweredby&amp;amp;utm_content=form&amp;amp;utm_medium=referral&amp;amp;utm_source=dynamic&quot;},&quot;recaptcha&quot;:{&quot;enabled&quot;:false},&quot;return_visitor&quot;:{&quot;action&quot;:&quot;show&quot;,&quot;custom_content&quot;:&quot;&quot;},&quot;slide_in&quot;:{&quot;display_in&quot;:&quot;bottom_right&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;sticky_bar&quot;:{&quot;display_in&quot;:&quot;top&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15}},&quot;version&quot;:&quot;5&quot;}&#39; min-width=&quot;400 500 600 700 800&quot;&gt;
  &lt;div data-style=&quot;clean&quot;&gt;
    &lt;div class=&quot;formkit-header&quot; data-element=&quot;header&quot;&gt;
      &lt;h2&gt;Want to receive an email when I post?&lt;/h2&gt;
    &lt;/div&gt;
    &lt;ul class=&quot;formkit-alert formkit-alert-error&quot; data-element=&quot;errors&quot; data-group=&quot;alert&quot;&gt;&lt;/ul&gt;
    &lt;div data-element=&quot;fields&quot; data-stacked=&quot;false&quot; class=&quot;seva-fields formkit-fields&quot;&gt;
      &lt;div class=&quot;formkit-field&quot;&gt;
        &lt;input class=&quot;formkit-input&quot; name=&quot;email_address&quot; aria-label=&quot;Email Address&quot; placeholder=&quot;Email Address&quot; required=&quot;&quot; type=&quot;email&quot;&gt;
      &lt;/div&gt;
      &lt;button data-element=&quot;submit&quot; class=&quot;formkit-submit&quot; style=&quot;background-color:#e06c75;font-weight:700&quot;&gt;
        &lt;div class=&quot;formkit-spinner&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span class=&quot;&quot;&gt;Subscribe&lt;/span&gt;
      &lt;/button&gt;
    &lt;/div&gt;
    &lt;div class=&quot;formkit-guarantee&quot; data-element=&quot;guarantee&quot;&gt;
      &lt;p&gt;&lt;em&gt;Free and without spam. You can also use my &lt;a href=&quot;https://nullderef.com/index.xml&quot; target=&quot;_blank&quot;&gt;RSS&lt;/a&gt;, &lt;a href=&quot;https://x.com/null_deref&quot; target=&quot;_blank&quot;&gt;X&lt;/a&gt;, or &lt;a href=&quot;https://mstdn.social/@nullderef&quot; target=&quot;_blank&quot;&gt;Mastodon&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
      &lt;p style=&quot;opacity:.6;font-size:16px&quot;&gt;&lt;em&gt;Powered by &lt;a href=&quot;https://kit.com/&quot; target=&quot;_black&quot;&gt;Kit&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;style&gt;.formkit-form[data-uid=&quot;3680381778&quot;] a{text-decoration:underline}.formkit-form[data-uid=&quot;3680381778&quot;] legend{border:none;font-size:inherit;margin-bottom:10px;padding:0;position:relative;display:table}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-checkboxes,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-select{width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{border:0;border-radius:var(--radius);color:#fff;cursor:pointer;display:inline-block;text-align:center;font-weight:500;cursor:pointer;margin-bottom:15px;overflow:hidden;padding:0;position:relative;vertical-align:middle}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover{outline:0}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover&gt;span{background-color:rgba(0,0,0,.1)}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit&gt;span{display:block;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out;padding:12px 24px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input{background:#fff;padding:12px;border:1px solid var(--border);border-radius:var(--radius);-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;line-height:1.4;margin:0;-webkit-transition:border-color ease-out .3s;transition:border-color ease-out .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:focus{outline:0;border-color:#1677be;-webkit-transition:border-color ease .3s;transition:border-color ease .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-webkit-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-moz-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:-ms-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert{background:#f9fafb;border:1px solid #e3e3e3;border-radius:5px;-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;list-style:none;margin:25px auto;padding:12px;text-align:center;width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert:empty{display:none}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-success{background:#d3fbeb;border-color:#10bf7a;color:#0c905c}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-error{background:#fde8e2;border-color:#f2643b;color:#ea4110}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;height:0;width:0;margin:0 auto;position:absolute;top:0;left:0;right:0;width:0;overflow:hidden;text-align:center;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div{margin:auto;width:12px;height:12px;background-color:#fff;opacity:.3;border-radius:100%;display:inline-block;-webkit-animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both;animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:first-child{-webkit-animation-delay:-.32s;animation-delay:-.32s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:nth-child(2){-webkit-animation-delay:-.16s;animation-delay:-.16s}.formkit-form[data-uid=&quot;3680381778&quot;]{border:1px solid var(--border);border-radius:var(--radius);position:relative;overflow:hidden;background:var(--code-bg)}.formkit-form[data-uid=&quot;3680381778&quot;]{padding:20px;width:100%;position:relative}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-header{margin:0 0 27px 0;font-size:20px;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee{margin-top:10px;margin-bottom:32px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee&gt;p{margin:0;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-fields{display:flex;flex-wrap:wrap;margin:25px auto 0 auto;gap:10px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-field,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{flex:1 0 100%}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;]{padding:0 30px}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-field{flex:100 1 auto}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-submit{flex:1 1 auto}&lt;/style&gt;
&lt;/form&gt;
&lt;h2 id=&quot;footnotes&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://doc.rust-lang.org/cargo/reference/profiles.html#release&quot;&gt;https://doc.rust-lang.org/cargo/reference/profiles.html#release&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/rust-features/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://doc.rust-lang.org/std/keyword.const.html&quot;&gt;https://doc.rust-lang.org/std/keyword.const.html&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/rust-features/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://doc.rust-lang.org/reference/const_eval.html&quot;&gt;https://doc.rust-lang.org/reference/const_eval.html&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/rust-features/#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</content>
  </entry>
  <entry>
    <title>Plugins in Rust: The Technologies</title>
    <link href="https://nullderef.com/blog/plugin-tech/" />
    <updated>2021-05-17T00:00:00Z</updated>
    <id>https://nullderef.com/blog/plugin-tech/</id>
    <content type="html">&lt;details class=&quot;toc&quot;&gt;
&lt;summary&gt;Table of Contents&lt;/summary&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#the-problem&quot;&gt;The Problem &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#safety-concerns&quot;&gt;Safety Concerns &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#unsafe-code&quot;&gt;Unsafe Code &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#error-resilience&quot;&gt;Error Resilience &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#remote-code-execution-via-plugins&quot;&gt;Remote Code Execution via Plugins &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#backward-compatibility&quot;&gt;Backward Compatibility &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#possible-solutions&quot;&gt;Possible Solutions &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#avoiding-breakage&quot;&gt;Avoiding Breakage &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#possible-approaches&quot;&gt;Possible Approaches &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#scripting-languages&quot;&gt;Scripting Languages &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#inter-process-communication&quot;&gt;Inter-Process Communication &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#dynamic-loading&quot;&gt;Dynamic Loading &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#webassembly&quot;&gt;WebAssembly &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#prior-art&quot;&gt;Prior Art &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#conclusion&quot;&gt;Conclusion &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/details&gt;
&lt;style&gt;.red{color:red}.olive{color:olive}.green{color:green}.white-background{background-color:#fff}.txt-round{border-radius:3px;padding:2px 6px}.txt-margin-right{margin-right:5px}.txt-margin-left{margin-left:5px}&lt;/style&gt;
&lt;p&gt;Welcome to the &lt;a href=&quot;https://nullderef.com/series/rust-plugins/&quot;&gt;“Plugins in Rust” series&lt;/a&gt;! During the next months I’ll be involved in a project with &lt;a href=&quot;https://www.tremor.rs/&quot;&gt;Tremor&lt;/a&gt;, for which I need to implement a Plugin System. The goal is to maximize modularity and reduce Rust’s wild compilation times. The implementation will end up being done with dynamic loading, but I will cover all the alternatives first to make sure it’s the best approach for my use-case.&lt;/p&gt;
&lt;p&gt;In this first article I will analyze the different ways in which our plugin system may be implemented in Rust. Note that I’m no expert in the topic; my objective is to merely collect all the information I’ve found so far and present it as a summary, with enough resources for the reader to learn more on their own and make their own decisions.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;requirements&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;the-problem&quot; tabindex=&quot;-1&quot;&gt;The Problem &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-tech/#the-problem&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This is actually part of my &lt;a href=&quot;https://summerofcode.withgoogle.com/&quot;&gt;Google Summer of Code&lt;/a&gt; project for &lt;a href=&quot;https://www.tremor.rs/&quot;&gt;Tremor&lt;/a&gt;. While this will specifically cover Tremor’s use case, I’m sure many other applications in the future will have similar requirements for their own program. And considering the few articles I found about plugins in Rust, this &lt;a href=&quot;https://nullderef.com/series/rust-plugins/&quot;&gt;series&lt;/a&gt; may be helpful to the community.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: Google didn’t end up allocating a slot for me in the GSoC, but I’ll still be able to continue through &lt;a href=&quot;https://mentorship.lfx.linuxfoundation.org/project/b90f7174-fc53-40bc-b9e2-9905f88c38ff&quot;&gt;a LFX Mentorship&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;After showing the proposal to the Tremor team, I was given an important suggestion: in this case there isn’t really a need to be able to write plugins from multiple programming languages; it’s not worth the additional complexity. It would definitely be interesting but with this I can focus on the main problems I discovered in my proposal: the ABI instability, and safety.&lt;/p&gt;
&lt;p&gt;Besides that, the rest of the main points to consider remain the same:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Mandatory:&lt;/strong&gt; Being able to load/unload the plugins both at start-time and at run-time.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mandatory:&lt;/strong&gt; Cross-platform support&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mandatory:&lt;/strong&gt; Low overhead&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mandatory:&lt;/strong&gt; Available at least from Rust&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extra:&lt;/strong&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#safety&quot;&gt;Safety&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extra:&lt;/strong&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#back-comp&quot;&gt;Backwards compatibility&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Extra&lt;/strong&gt;: Not much work to port from the existing implementations&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a name=&quot;safety&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;safety-concerns&quot; tabindex=&quot;-1&quot;&gt;Safety Concerns &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-tech/#safety-concerns&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;I’ve been wanting to take a look at the possible safety concerns a plugin system could bring to Tremor since the initial proposal.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: Depending on the approach that is taken for the Plugin System, implementing solutions to some of the points I make below can be really complex, so I wouldn’t work on them for the initial deadline. For now, it’s something we have to be aware of.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_unsafe_code&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;unsafe-code&quot; tabindex=&quot;-1&quot;&gt;Unsafe Code &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-tech/#unsafe-code&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Many of the technologies that can be used to implement a Plugin System work with unsafe code, like &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#ffi&quot;&gt;FFIs&lt;/a&gt; or &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#memory-sharing&quot;&gt;IPC Memory Sharing&lt;/a&gt;. This isn’t necessarily a problem if it’s self-contained and thoroughly reviewed, but we lose some safety guarantees provided by Rust, increasing the maintenance cost of the library.&lt;/p&gt;
&lt;p&gt;It takes considerably more work to make sure the implementation is sound, even with tools like &lt;a href=&quot;https://github.com/rust-lang/miri&quot;&gt;MIRI&lt;/a&gt;, which I plan on integrating into Tremor if I do end up having to use &lt;code&gt;unsafe&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_error_resilience&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;error-resilience&quot; tabindex=&quot;-1&quot;&gt;Error Resilience &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-tech/#error-resilience&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Rust doesn’t protect its users from leaking memory. In fact, it’s as easy as calling &lt;code&gt;mem::forget&lt;/code&gt;. The thing is that if a plugin leaks memory, the entire process also does, meaning that Tremor’s performance could be affected by incorrectly developed plugins.&lt;/p&gt;
&lt;p&gt;This doesn’t happen just with memory leaks; a plugin could abort or panic and crash the entire thing very easily.&lt;/p&gt;
&lt;p&gt;Ideally, Tremor could detect plugins that aren’t performing well and stop them before it’s too late. The core of the program could continue running even when a plugin fails, and perhaps warn the user about its malfunctioning, for optimal error resilience.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_remote_code_execution_via_plugins&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;remote-code-execution-via-plugins&quot; tabindex=&quot;-1&quot;&gt;Remote Code Execution via Plugins &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-tech/#remote-code-execution-via-plugins&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://en.wikipedia.org/wiki/Component_Object_Model#Security&quot;&gt;This was a problem with Internet Explorer&lt;/a&gt;, for example. It used COM and ActiveX, which implemented no sandboxing at all and were running directly on the user’s machine, allowing malware to be included in plugins with the ability to execute arbitrary code on the host machine. This can be less of a problem when installing only trusted plugins via digital signatures, but it’s still a huge risk.&lt;/p&gt;
&lt;p&gt;In the case of Tremor plugins this is a similar problem. The end user — those who will add plugins to their configuration — is a developer, who should be more wary of what they’re including in their own projects. But the truth is that it isn’t any better.&lt;/p&gt;
&lt;p&gt;I would compare the situation more specifically to how package managers like &lt;a href=&quot;https://www.npmjs.com/&quot;&gt;npm&lt;/a&gt; work (or anything that deals with dependencies, really). The entire infrastructure is usually based on trust; noone’s really stopping you from uploading a malicious package/crate for remote code execution or to steal data&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fn2&quot; id=&quot;fnref2&quot;&gt;[2]&lt;/a&gt;&lt;/sup&gt;. Plugins are like dependencies in this case; they have full access to the host’s computer, and thus shouldn’t be trusted by default.&lt;/p&gt;
&lt;p&gt;An improved approach to Node + npm would be something like &lt;a href=&quot;https://github.com/denoland/deno&quot;&gt;Deno&lt;/a&gt;, which is a secure by default JavaScript/TypeScript runtime. This is enabled thanks to sandboxing, and requires the developer to manually toggle, e.g., access to the filesystem or to the network. This is no panacea because people might end up enabling whatever permission is required by the dependency they want to install without thinking about it, but it’s similar to &lt;code&gt;unsafe&lt;/code&gt;: at least it makes you aware that you might mess up.&lt;/p&gt;
&lt;p&gt;One could argue that their program is realistically going to be running in a Virtual Machine or a container most of the times, where this may not matter so much. But should you really assume that? Should user safety rely on the fact that the machine is running on an isolated kernel? Containers, by the way, introduce a much bigger overhead in comparison to some sandboxing methods. And even if the system as a whole is isolated, there is still a possibility for internal leaks: the Postgres plugin has access to whatever is being used by the Apache Kafka plugin, which may have sensitive logs you don’t want anyone to see.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;back-comp&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;backward-compatibility&quot; tabindex=&quot;-1&quot;&gt;Backward Compatibility &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-tech/#backward-compatibility&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;There needs to be some kind of version handling within this project. The core of Tremor may change with frequency, which will break plugins based on previous versions. If the plugin receives a struct from the core, but the struct had one of its members removed in a new version, it will result in undefiend behaviour.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_possible_solutions&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;possible-solutions&quot; tabindex=&quot;-1&quot;&gt;Possible Solutions &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-tech/#possible-solutions&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;The easiest fix for backward compatibility issues I can come up with is to serialize and deserialize the data with a flexible protocol rather than using its binary representation directly. If something like JSON is used to communicate between the core and its plugins, adding a field to a message won’t break anything, and removing one can be done via a deprecation process. Unfortunately, this introduces some overhead the application may not be interested in.&lt;/p&gt;
&lt;p&gt;Other more involved methods for binary representations include&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fn3&quot; id=&quot;fnref3&quot;&gt;[3]&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Reserving space in the struct for future use.&lt;/li&gt;
&lt;li&gt;Making the struct an opaque type, requiring function calls to get the fields.&lt;/li&gt;
&lt;li&gt;Giving the struct a pointer to its “version 2” data (opaque in “version 1”).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a name=&quot;_avoiding_breakage&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;avoiding-breakage&quot; tabindex=&quot;-1&quot;&gt;Avoiding Breakage &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-tech/#avoiding-breakage&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;There are times when breakage is inevitable. Tremor may want to rewrite part of its interface or finally remove a deprecated feature without being afraid of breaking all the plugins previously developed.&lt;/p&gt;
&lt;p&gt;For that, the plugins must embed a bit of metadata about the different versions of rustc/interface/etc it was developed for. Then, when it’s loaded by Tremor, it may check if they’re compatible, rather than breaking in mysterious ways.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;_possible_approaches&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;possible-approaches&quot; tabindex=&quot;-1&quot;&gt;Possible Approaches &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-tech/#possible-approaches&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The following are the most viable technologies that could be used as the base of the system. Some of them won’t match the requirements I mentioned earlier at first glance, but it’s a good idea to at least &lt;em&gt;consider&lt;/em&gt; all of them. I haven’t written a line of code yet, so if an approach were to catch someone’s eye we could investigate more about it. I will rate each of the alternatives in a scale from 1 to 5 (approximately) in order to easen the decision-taking.&lt;/p&gt;
&lt;p&gt;&lt;a name=&quot;scripting-langs&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;scripting-languages&quot; tabindex=&quot;-1&quot;&gt;Scripting Languages &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-tech/#scripting-languages&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Plenty of projects use scripting languages to extend their functionality at runtime, like Python, Ruby, Perl, Bash, or JavaScript. Most notably, Vim created its own scripting language, Vimscript, in order to be fully customizable, and NeoVim is now pushing &lt;a href=&quot;https://www.lua.org/&quot;&gt;Lua&lt;/a&gt; as a first-class language for configuration. Even Tremor itself has the interpreted query language &lt;a href=&quot;https://www.tremor.rs/docs/language/&quot;&gt;Troy&lt;/a&gt; for configuration.&lt;/p&gt;
&lt;p&gt;Lua is seen in game development; it’s a quite simple language with a very performant JIT implementation, which in any case I think would be the best option here. It could be embedded into our runtime (it’s only 281 kB compiled!&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fn4&quot; id=&quot;fnref4&quot;&gt;[4]&lt;/a&gt;&lt;/sup&gt;) and used to load plugins at either start-time or run-time. And knowing it’s used in games, which are &lt;em&gt;obsessed&lt;/em&gt; with performance, it might not be that much of a big deal in that regard.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: There are &lt;a href=&quot;https://arewegameyet.rs/ecosystem/scripting/&quot;&gt;languages specifically designed to extend Rust’s capabilities&lt;/a&gt;, which you might be interested in, but I’ll just simplify this part by going for Lua.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Rust has libraries like &lt;a href=&quot;https://github.com/amethyst/rlua&quot;&gt;&lt;code&gt;rlua&lt;/code&gt;&lt;/a&gt; which provide bindings for interoperability with Lua. &lt;code&gt;rlua&lt;/code&gt; in particular seems to focus on having an idiomatic and safe interface, which is rare for a bindings library and good news, though it seems to be currently semi-abandoned, and forked by &lt;a href=&quot;https://github.com/khvzak/mlua&quot;&gt;&lt;code&gt;mlua&lt;/code&gt;&lt;/a&gt;. Unfortunately, after digging a bit the Rust ecosystem for Lua bindings doesn’t seem to be as mature as I’d like for a project this big; there’s still some work to do to reach more stability.&lt;/p&gt;
&lt;p&gt;Lua gets extra points in safety. It’s possible to sandbox it by blocking whichever functions you don’t want users to access&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fn5&quot; id=&quot;fnref5&quot;&gt;[5]&lt;/a&gt;&lt;/sup&gt; (though it’s “tricky and generally speaking difficult to get right”). Similarly, one can also handle resource exhaustion issues within Lua programs. Not to mention that this wouldn’t require &lt;code&gt;unsafe&lt;/code&gt; at all with an optimal set of bindings.&lt;/p&gt;
&lt;p&gt;Anyhow, the main deal-breaker with scripting languages in this case is that it would be extremely complicated to port everything in Tremor so that it can be used from Lua. For new projects this could perhaps be something interesting, but not if the entirety of the already existing plugins have to be rewritten.&lt;/p&gt;
&lt;p&gt;Still, it’s a pretty interesting option for new projects, as you have ABI stability guarantees, solid safety overall, and it’s very straightforward to use.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong class=&quot;green white-background txt-margin-right txt-round&quot;&gt;5/5&lt;/strong&gt; &lt;strong&gt;Cross-platform support&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;green white-background txt-margin-right txt-round&quot;&gt;4/5&lt;/strong&gt; &lt;strong&gt;Low overhead&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;olive white-background txt-margin-right txt-round&quot;&gt;3/5&lt;/strong&gt; &lt;strong&gt;Rust availability&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;green white-background txt-margin-right txt-round&quot;&gt;5/5&lt;/strong&gt; &lt;strong&gt;Safety&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;green white-background txt-margin-right txt-round&quot;&gt;5/5&lt;/strong&gt; &lt;strong&gt;Backwards compatibility&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;red white-background txt-margin-right txt-round&quot;&gt;0/5&lt;/strong&gt; &lt;strong&gt;Ease of porting existing implementations&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a name=&quot;ipc&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;inter-process-communication&quot; tabindex=&quot;-1&quot;&gt;Inter-Process Communication &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-tech/#inter-process-communication&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Another possibility for plugins is to define a protocol for Inter-Process Communication, turning your program into a server that extends its capabilities by connecting to external plugins. For instance, most text editors use this method to support the &lt;a href=&quot;https://microsoft.github.io/language-server-protocol/&quot;&gt;Language Server Protocol&lt;/a&gt;, which uses JSON-RPC.&lt;/p&gt;
&lt;p&gt;There are three ways to do IPC, which I’ll briefly list below. Performance-wise, this graph shows a comparison of the overhead of each of them&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fn6&quot; id=&quot;fnref6&quot;&gt;[6]&lt;/a&gt;&lt;/sup&gt;:&lt;/p&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/blog/plugin-tech/7UXWJEWW8N-1170.avif 1170w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/blog/plugin-tech/7UXWJEWW8N-1170.webp 1170w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; alt=&quot;IPC comparison&quot; src=&quot;https://nullderef.com/blog/plugin-tech/7UXWJEWW8N-1170.png&quot; style=&quot;padding:0 20%&quot; width=&quot;1170&quot; height=&quot;845&quot;&gt;&lt;/picture&gt;
&lt;p&gt;&lt;a name=&quot;sockets&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;option-1-based-on-sockets&quot; tabindex=&quot;-1&quot;&gt;Option 1: Based on Sockets &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-tech/#option-1-based-on-sockets&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Sockets are the “worst”-performing alternative in the previous chart, but they’re so common and easy to use in most languages that it’s worth taking a look at. Using relatively lightweight protocols like &lt;a href=&quot;https://developers.google.com/protocol-buffers&quot;&gt;Protocol Buffers&lt;/a&gt;, the performance would be close to passing raw structs, but with improved backwards/forwards compatibility&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fn7&quot; id=&quot;fnref7&quot;&gt;[7]&lt;/a&gt;&lt;/sup&gt;. JSON would probably not make that big of a difference in terms of performance either. This would make it possible to write a plugin in any language as well — including Rust — as long as there’s an implementation of the protocol available. But there’s still noticeable overhead when communicating via sockets; sending and receiving the messages can be much costlier than just calling a function, even if this happens on localhost.&lt;/p&gt;
&lt;p&gt;This alternative is much more interesting than &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#scripting-langs&quot;&gt;Scripting Languages&lt;/a&gt; for Tremor’s specific case: we don’t have to completely rewrite everything, since Rust can still be used, and implementing the protocol to communicate between the Tremor core and its plugins should be as easy as &lt;code&gt;#[derive(Serialize)]&lt;/code&gt; for sending and &lt;code&gt;#[derive(Deserialize)]&lt;/code&gt; for receiving.&lt;/p&gt;
&lt;p&gt;As to safety, separate processes imply that malfunctioning plugins don’t affect Tremor directly, and the system basically consists on implementing servers, which require no &lt;code&gt;unsafe&lt;/code&gt; at all and has much more popularity and support in Rust. It’s still hard to properly sandbox the plugins, though.&lt;/p&gt;
&lt;p&gt;Overall, I consider this a very solid solution, with its main drawback being performance. I can’t really guess the effect this would have in Tremor’s speed, so I would love to create a quick benchmark when I get to implement the first prototypes to see if it’s actually the best choice, if Tremor devs think it’s worth my time.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong class=&quot;green white-background txt-margin-right txt-round&quot;&gt;5/5&lt;/strong&gt; &lt;strong&gt;Cross-platform support&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;olive white-background txt-margin-right txt-round&quot;&gt;3/5&lt;/strong&gt; &lt;strong&gt;Low overhead&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;green white-background txt-margin-right txt-round&quot;&gt;5/5&lt;/strong&gt; &lt;strong&gt;Rust availability&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;olive white-background txt-margin-right txt-round&quot;&gt;3/5&lt;/strong&gt; &lt;strong&gt;Safety&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;green white-background txt-margin-right txt-round&quot;&gt;5/5&lt;/strong&gt; &lt;strong&gt;Backwards compatibility&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;green white-background txt-margin-right txt-round&quot;&gt;5/5&lt;/strong&gt; &lt;strong&gt;Ease of porting existing implementations&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a name=&quot;pipes&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;option-2-based-on-pipes&quot; tabindex=&quot;-1&quot;&gt;Option 2: Based on Pipes &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-tech/#option-2-based-on-pipes&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Pipes have always been fairly popular specifically on Unix systems, and enable Inter-Process Communication with less overhead than sockets. They are made to be ran on the same machine, which is exactly what we need. The terminal file manager &lt;a href=&quot;https://github.com/jarun/nnn&quot;&gt;nnn&lt;/a&gt; uses this approach: plugins can read from a FIFO (Named Pipe) to receive selections from nnn (lists of files or directories) and act accordingly.&lt;/p&gt;
&lt;p&gt;The rest is basically the same as with &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#sockets&quot;&gt;Sockets&lt;/a&gt;, maybe with extra points for performance, and less for Rust availability, since there don’t seem to be any reliable libraries for pipes (maybe &lt;a href=&quot;https://docs.rs/interprocess/1.1.1/interprocess/&quot;&gt;&lt;code&gt;interprocess&lt;/code&gt;&lt;/a&gt; or &lt;a href=&quot;https://crates.io/crates/ipipe&quot;&gt;&lt;code&gt;ipipe&lt;/code&gt;&lt;/a&gt;). But really, are libraries necessary at all? The &lt;code&gt;std&lt;/code&gt; library &lt;a href=&quot;https://doc.rust-lang.org/rust-by-example/std_misc/process/pipe.html&quot;&gt;has support for cross-platform pipes when executing external commands&lt;/a&gt; for stdin, stdout, and stderr, which most times is enough. The plugin can just use stdin to receive messages and stdout to send them. If that’s enough for your case then it’s vastly simplified.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong class=&quot;green white-background txt-margin-right txt-round&quot;&gt;5/5&lt;/strong&gt; &lt;strong&gt;Cross-platform support&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;green white-background txt-margin-right txt-round&quot;&gt;4/5&lt;/strong&gt; &lt;strong&gt;Low overhead&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;green white-background txt-margin-right txt-round&quot;&gt;4/5&lt;/strong&gt; &lt;strong&gt;Rust availability&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;olive white-background txt-margin-right txt-round&quot;&gt;3/5&lt;/strong&gt; &lt;strong&gt;Safety&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;green white-background txt-margin-right txt-round&quot;&gt;5/5&lt;/strong&gt; &lt;strong&gt;Backwards compatibility&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;green white-background txt-margin-right txt-round&quot;&gt;5/5&lt;/strong&gt; &lt;strong&gt;Ease of porting existing implementations&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a name=&quot;memory-sharing&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h4 id=&quot;option-3-based-on-memory-sharing&quot; tabindex=&quot;-1&quot;&gt;Option 3: Based on Memory Sharing &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-tech/#option-3-based-on-memory-sharing&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h4&gt;
&lt;p&gt;Knowing that the plugins are intended to be on the same machine as the core of Tremor, there’s no need to actually send and receive messages. One can share memory between multiple processes and send notifications to receive updates. The performance is comparable to using FFI, since the only overhead is the initial cost from setting up the shared pages, having regular memory access afterwards&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fn8&quot; id=&quot;fnref8&quot;&gt;[8]&lt;/a&gt;&lt;/sup&gt;.&lt;/p&gt;
&lt;p&gt;This feature heavily depends on the system’s kernel, so it may hurt the “Cross-Compatibility” requirement. We have libraries like &lt;a href=&quot;https://docs.rs/shared_memory&quot;&gt;&lt;code&gt;shared_memory&lt;/code&gt;&lt;/a&gt; + &lt;a href=&quot;https://docs.rs/raw_sync&quot;&gt;&lt;code&gt;raw_sync&lt;/code&gt;&lt;/a&gt; in Rust that wrap all the OS implementations under the same interface, but admittedly, they don’t seem anywhere near as popular as most of the other alternatives. Not to mention that the examples for &lt;code&gt;shared_memory&lt;/code&gt; &lt;em&gt;do&lt;/em&gt; use &lt;code&gt;unsafe&lt;/code&gt;, and a lot of it.&lt;/p&gt;
&lt;p&gt;Maybe if it was easier to use this would be a good idea, but IPC shared memory doesn’t seem to be any better than FFIs overall.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong class=&quot;green white-background txt-margin-right txt-round&quot;&gt;5/5&lt;/strong&gt; &lt;strong&gt;Cross-platform support&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;green white-background txt-margin-right txt-round&quot;&gt;5/5&lt;/strong&gt; &lt;strong&gt;Low overhead&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;red white-background txt-margin-right txt-round&quot;&gt;2/5&lt;/strong&gt; &lt;strong&gt;Rust availability&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;red white-background txt-margin-right txt-round&quot;&gt;2/5&lt;/strong&gt; &lt;strong&gt;Safety&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;olive white-background txt-margin-right txt-round&quot;&gt;3/5&lt;/strong&gt; &lt;strong&gt;Backwards compatibility&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;green white-background txt-margin-right txt-round&quot;&gt;5/5&lt;/strong&gt; &lt;strong&gt;Ease of porting existing implementations&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a name=&quot;ffi&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;dynamic-loading&quot; tabindex=&quot;-1&quot;&gt;Dynamic Loading &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-tech/#dynamic-loading&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;This is possibly the &lt;em&gt;least weird&lt;/em&gt; way to implement a Plugin Development Kit, i.e., it’s the most popular method I’ve seen outside of Rust. A &lt;em&gt;Foreign Function Interface&lt;/em&gt; can allow us to directly access resources in separately compiled objects, even after the linking phase with dynamic loading. It’s one of the fastest options available because there’s no overhead at all after dynamically loading the library.&lt;/p&gt;
&lt;p&gt;The main library for this is &lt;a href=&quot;https://github.com/nagisa/rust_libloading&quot;&gt;&lt;code&gt;libloading&lt;/code&gt;&lt;/a&gt;. There’s also the less popular &lt;a href=&quot;https://docs.rs/dlopen/0.1.8/dlopen/&quot;&gt;&lt;code&gt;dlopen&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://github.com/Tyleo/sharedlib&quot;&gt;&lt;code&gt;sharedlib&lt;/code&gt;&lt;/a&gt;, with some small differences&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fn9&quot; id=&quot;fnref9&quot;&gt;[9]&lt;/a&gt;&lt;/sup&gt;. It seems to be a lower-level implementation for any kind of FFI that requires &lt;code&gt;unsafe&lt;/code&gt; for almost everything — what I was expecting. Based on it there’s &lt;a href=&quot;https://github.com/emoon/dynamic_reload&quot;&gt;&lt;code&gt;dynamic_reload&lt;/code&gt;&lt;/a&gt;, which is very interesting in order to “live reload” the plugins when they are recompiled. This would be useful for the development process of the plugins, since it also handles unloading the &lt;em&gt;same plugin&lt;/em&gt; seamlessly, but that’s not a goal for this project, so I don’t plan on using it. And the &lt;a href=&quot;https://github.com/vberger/dlib&quot;&gt;&lt;code&gt;dlib&lt;/code&gt;&lt;/a&gt; crate provides macros to make the library loading simpler, based on &lt;code&gt;libloading&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Creating an internal interface for Tremor through C is a lot of work. But it’s unfortunately the only way to do it, since Rust-to-Rust FFI is not stable. It has awful safety by relying heavily on &lt;code&gt;unsafe&lt;/code&gt; with plenty of caveats&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fn10&quot; id=&quot;fnref10&quot;&gt;[10]&lt;/a&gt;&lt;/sup&gt;&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fn11&quot; id=&quot;fnref11&quot;&gt;[11]&lt;/a&gt;&lt;/sup&gt;, including subtle differences between Operating Systems&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fn12&quot; id=&quot;fnref12&quot;&gt;[12]&lt;/a&gt;&lt;/sup&gt; — although &lt;code&gt;dlopen&lt;/code&gt; seems to be better in that regard&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fn13&quot; id=&quot;fnref13&quot;&gt;[13]&lt;/a&gt;&lt;/sup&gt;. Since it doesn’t have a sandbox, plugins can abort Tremor’s core execution when panicking/leaking memory/similars.&lt;/p&gt;
&lt;p&gt;I recently discovered the &lt;a href=&quot;https://github.com/rodrimati1992/abi_stable_crates&quot;&gt;&lt;code&gt;abi_stable&lt;/code&gt;&lt;/a&gt; crate, which guarantees stability in Rust’s ABI unofficially and helps a lot with the binary compatibility aspect.&lt;/p&gt;
&lt;p&gt;It includes FFI-safe alternatives to many of the types in the standard library, and even external ones (namely &lt;code&gt;crossbeam&lt;/code&gt;, &lt;code&gt;parking_lot&lt;/code&gt; and &lt;code&gt;serde_json&lt;/code&gt;). This works by implementing a &lt;a href=&quot;https://docs.rs/abi_stable/*/abi_stable/abi_stability/stable_abi_trait/trait.StableAbi.html&quot;&gt;&lt;code&gt;StableAbi&lt;/code&gt;&lt;/a&gt; trait that guarantees its FFI-safety, which may be done automatically with one of its procedural macros. Internal ABI stability is also guaranteed with macros like &lt;code&gt;#[sabi(last_prefix_field)]&lt;/code&gt;, which would allow Tremor to add fields to existing structs &lt;a href=&quot;https://github.com/rodrimati1992/abi_stable_crates/blob/19d71ec9175f870c1c784c37dae730b99948cacf/examples/0_modules_and_interface_types/interface/src/lib.rs#L45&quot;&gt;without breaking backwards compatibility&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Fortunately, this crate has a &lt;a href=&quot;https://github.com/rodrimati1992/abi_stable_crates/tree/master/examples&quot;&gt;few very detailed examples&lt;/a&gt; one can look at to better understand how it works, and it’s exceptionally well documented. If I’ve understood it correctly, some of its inconvenients are the following:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;You have to use the types from &lt;code&gt;abi_stable&lt;/code&gt; instead of &lt;code&gt;std&lt;/code&gt; for the values passed through FFI.&lt;/li&gt;
&lt;li&gt;The whole crate seems huge and would probably add considerable complexity to this FFI method.&lt;/li&gt;
&lt;li&gt;It’s worth mentioning that library unloading is a non-feature; although it’s not going to be implemented for this project, it might in the future.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;While it’s a fascinating concept and look forward to seeing how it evolves, I’m not a big fan of having to resort to it. And the fact that it’s unofficial and not that popular doesn’t give me full confidence that this will still work in 5 years, or that it won’t be outdated/abandoned.&lt;/p&gt;
&lt;p&gt;More people have tried writing Rust FFIs in the past, so we can take a look at existing tutorials in order to see their experience:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;The one and only Amos wrote an extremely detailed blog post on fasterthanlime &lt;a href=&quot;https://fasterthanli.me/articles/so-you-want-to-live-reload-rust&quot;&gt;here&lt;/a&gt;, specifically about live reloading Rust — a closely related topic.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Michael Bryan made a guided introduction to Plugins in Rust &lt;a href=&quot;https://adventures.michaelfbryan.com/posts/plugins-in-rust/&quot;&gt;here&lt;/a&gt;, and also wrote a tutorial for his unofficial Rust FFI book &lt;a href=&quot;https://michael-f-bryan.github.io/rust-ffi-guide/dynamic_loading.html&quot;&gt;here&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/zicklag&quot;&gt;@zicklag&lt;/a&gt;, who had read Michael’s article, tried it by himself in order to add a plugin system to &lt;a href=&quot;https://github.com/amethyst/amethyst&quot;&gt;Amethyst&lt;/a&gt;, and posted &lt;a href=&quot;https://zicklag.github.io/rust-tutorials/rust-plugins.html&quot;&gt;this tutorial&lt;/a&gt;. When he shared the post on the official Rust forum, it was accompanied by &lt;a href=&quot;https://users.rust-lang.org/t/creating-rust-apps-with-dynamically-loaded-rust-plugins/28814/111092&quot;&gt;this demotivating comment&lt;/a&gt;, after failing&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fn14&quot; id=&quot;fnref14&quot;&gt;[14]&lt;/a&gt;&lt;/sup&gt; to implement it for Amethyst:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Unfortunately I found that dynamic linking doesn’t actually work in Rust across different versions of Rust, and the technique for plugins also failed, even inside the same version of Rust, when I tried to compile an app with other dependencies like Amethyst. That leaves the technique outlined in the tutorial not very practical for real applications.&lt;/p&gt;
&lt;p&gt;The closest thing I’ve found to accomplish something similar is [&lt;code&gt;abi_stable&lt;/code&gt;].&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;He also added &lt;a href=&quot;https://users.rust-lang.org/t/creating-rust-apps-with-dynamically-loaded-rust-plugins/28814/7&quot;&gt;later on&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;It could very well be possible [to use WebAssembly here]. It wouldn’t be exactly the same workflow, but I’ve considered using Wasmtime or CraneLift, which Wasmtime is built on, to Run Wasm modules as plugins.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;He didn’t have time to end up doing so, so we’ll have to investigate ourselves.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;So, more or less:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong class=&quot;olive white-background txt-margin-right txt-round&quot;&gt;3/5&lt;/strong&gt; &lt;strong&gt;Cross-platform support&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;green white-background txt-margin-right txt-round&quot;&gt;5/5&lt;/strong&gt; &lt;strong&gt;Low overhead&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;green white-background txt-margin-right txt-round&quot;&gt;5/5&lt;/strong&gt; &lt;strong&gt;Rust availability&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;red white-background txt-margin-right txt-round&quot;&gt;1/5&lt;/strong&gt; &lt;strong&gt;Safety&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;red white-background txt-margin-right txt-round&quot;&gt;0/5&lt;/strong&gt; &lt;strong&gt;Backwards compatibility&lt;/strong&gt; (maybe &lt;strong class=&quot;green white-background txt-margin-right txt-round&quot;&gt;5/5&lt;/strong&gt; if using &lt;code&gt;abi_stable&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;green white-background txt-margin-right txt-round&quot;&gt;5/5&lt;/strong&gt; &lt;strong&gt;Ease of porting existing implementations&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a name=&quot;wasm&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3 id=&quot;webassembly&quot; tabindex=&quot;-1&quot;&gt;WebAssembly &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-tech/#webassembly&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;p&gt;Now, this is what I wanted to emphasize in this article! Turns out WebAssembly isn’t limited to web development anymore; it’s slowly evolving into a portable binary-code format. As far as I know, this should be like a mix between &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#ffi&quot;&gt;Dynamic Loading&lt;/a&gt; and &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#scripting-langs&quot;&gt;Scripting Languages&lt;/a&gt;, with a stronger focus in stability and portability. Here’s what &lt;a href=&quot;https://en.wikipedia.org/wiki/WebAssembly&quot;&gt;Wikipedia has to say about it&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;WebAssembly (sometimes abbreviated Wasm) is an open standard that defines a portable binary-code format for executable programs, and a corresponding textual assembly language, as well as interfaces for facilitating interactions between such programs and their host environment. The main goal of WebAssembly is to enable high-performance applications on web pages, &lt;mark&gt;but the format is designed to be executed and integrated in other environments as well, including standalone ones.&lt;/mark&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;So to clear it up, &lt;strong&gt;Wasm&lt;/strong&gt; is an &lt;em&gt;assembly language&lt;/em&gt;, and &lt;a href=&quot;https://wasi.dev/&quot;&gt;&lt;strong&gt;WASI&lt;/strong&gt;&lt;/a&gt; is a &lt;em&gt;system interface&lt;/em&gt; to run it outside the web. The latter is extremely well explained &lt;a href=&quot;https://hacks.mozilla.org/2019/03/standardizing-wasi-a-webassembly-system-interface/&quot;&gt;in this article by Mozilla&lt;/a&gt;, I suggest giving it a read for more details. &lt;a href=&quot;https://bytecodealliance.org/articles/announcing-the-bytecode-alliance&quot;&gt;This one&lt;/a&gt; is also very nice to read and explains the isolation system it provides, specifically.&lt;/p&gt;
&lt;p&gt;The two main points WebAssembly offers are, in a nutshell:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;When compiled, it doesn’t need to know what Operating System is being targeted. This is handled by the runtime, and the binary itself is fully portable.&lt;/li&gt;
&lt;li&gt;In order to handle untrustworthy programs, it implements a sandbox. With that, the host can limit exactly what a program has access to.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;WASI is just a standard, so it has multiple runtimes available. The most popular ones are coincidentally written in Rust: &lt;a href=&quot;https://github.com/bytecodealliance/wasmtime&quot;&gt;&lt;strong&gt;wasmtime&lt;/strong&gt;&lt;/a&gt; and &lt;a href=&quot;https://github.com/wasmerio/wasmer&quot;&gt;&lt;strong&gt;wasmer&lt;/strong&gt;&lt;/a&gt;. Both use the &lt;a href=&quot;https://github.com/bytecodealliance/wasmtime/blob/main/cranelift/README.md&quot;&gt;Cranelift&lt;/a&gt; backend to generate the WebAssembly machine code, though wasmer seems to also support LLVM and others. Then, the runtime can be used to run the generated &lt;code&gt;.wasm&lt;/code&gt; binary as a CLI or a library. This also means that plugins could be written in any language that compiles to WebAssembly, though it is frequently harder than compiling to native code. Many Rust libraries don’t support WebAssembly, so it could be tricky depending on the situation.&lt;/p&gt;
&lt;p&gt;The differences between the two runtimes aren’t that big. You can read &lt;a href=&quot;https://wiki.alopex.li/ActuallyUsingWasm&quot;&gt;this wiki article for more details, including examples&lt;/a&gt;, but I particularly liked this quote:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Just based on what they demonstrate, wasmer is more focused on embedding wasm in your native program, while wasmtime is more focused on executing standalone wasm programs using WASI. Both are capable of both, it just seems a matter of emphasis.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;The article also includes a &lt;s&gt;not reliable at all&lt;/s&gt; benchmark, which can serve us as a way to compare its performance with the native code you’d get with e.g., &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#ffi&quot;&gt;Dynamic Loading&lt;/a&gt;. It estimates that Wasm is a bit less than an order of magnitude slower than native code, and the same applies to memory usage. &lt;a href=&quot;https://github.com/jedisct1/webassembly-benchmarks/tree/master/2021-Q1&quot;&gt;A more thorough benchmark&lt;/a&gt; was done in libsodium that shows better results: Wasm can be just about 3 times slower than native code. Do note that this depends on the runtime that’s being used, and it may improve in the future, as WebAssembly is just 4 years old.&lt;/p&gt;
&lt;p&gt;There’s a whole series on how to make a Plugins System with Wasmer &lt;a href=&quot;https://freemasen.com/blog/wasmer-plugin-pt-1/&quot;&gt;here&lt;/a&gt;, which will come in handy to know what to expect. The usability doesn’t actually seem to be that good, since by default you can only use integers, floating-point or vectors&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fn15&quot; id=&quot;fnref15&quot;&gt;[15]&lt;/a&gt;&lt;/sup&gt; as parameters when calling Wasm plugins. For more complex types, you have to resort to encoding and decoding via a crate like &lt;a href=&quot;https://github.com/bincode-org/bincode&quot;&gt;&lt;code&gt;bincode&lt;/code&gt;&lt;/a&gt;, although most of the boilerplate can be reduced with procedural macros or a wrapper like &lt;a href=&quot;https://github.com/alec-deason/wasm_plugin&quot;&gt;&lt;code&gt;wasm_plugin&lt;/code&gt;&lt;/a&gt;, and this opens up the possibility of using serialization with support for backwards compatibility within Tremor. The &lt;a href=&quot;https://freemasen.com//blog/wasmer-plugin-pt-4/index.html&quot;&gt;last part&lt;/a&gt; of the series is the most interesting one, as it includes a real-world example, with a version of the final code in &lt;a href=&quot;https://github.com/FreeMasen/wasmer-plugin&quot;&gt;this repository&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;All in all, WebAssembly seems to win against &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#ffi&quot;&gt;Dynamic Loading&lt;/a&gt; in terms of security by not needing &lt;code&gt;unsafe&lt;/code&gt; at all and including sandboxing by default, at the cost of efficiency. This is up to the managers of the project and what they consider more important.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong class=&quot;green white-background txt-margin-right txt-round&quot;&gt;5/5&lt;/strong&gt; &lt;strong&gt;Cross-platform support&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;olive white-background txt-margin-right txt-round&quot;&gt;3/5&lt;/strong&gt; &lt;strong&gt;Low overhead&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;green white-background txt-margin-right txt-round&quot;&gt;5/5&lt;/strong&gt; &lt;strong&gt;Rust availability&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;green white-background txt-margin-right txt-round&quot;&gt;5/5&lt;/strong&gt; &lt;strong&gt;Safety&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;green white-background txt-margin-right txt-round&quot;&gt;4/5&lt;/strong&gt; &lt;strong&gt;Backwards compatibility&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong class=&quot;olive white-background txt-margin-right txt-round&quot;&gt;3/5&lt;/strong&gt; &lt;strong&gt;Ease of porting existing implementations&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;a name=&quot;_prior_art&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;prior-art&quot; tabindex=&quot;-1&quot;&gt;Prior Art &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-tech/#prior-art&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;It’s very important to take a look at projects that have already done this in the past in order to learn from their mistakes and not start from scratch.&lt;/p&gt;
&lt;p&gt;Here’s a list of some of the libraries I found with Plugin Systems, specifically written in Rust:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/rust-lang/cargo&quot;&gt;&lt;code&gt;cargo&lt;/code&gt;&lt;/a&gt;, &lt;a href=&quot;https://github.com/rust-lang/mdBook&quot;&gt;&lt;code&gt;mdbook&lt;/code&gt;&lt;/a&gt;: both have an extension system via CLI commands. Adding a subcommand to either of these utilities is as easy as creating a binary with a fixed prefix (e.g., &lt;code&gt;cargo-expand&lt;/code&gt;), and if it’s available in the &lt;code&gt;$PATH&lt;/code&gt; when running &lt;code&gt;cargo&lt;/code&gt;, it will be possible to invoke the plugin with &lt;code&gt;cargo expand&lt;/code&gt; as well.&lt;/p&gt;
&lt;p&gt;This is a very interesting approach, specially because of how simple it is to use. Cargo doesn’t seem to need to communicate with the extension at all, but &lt;code&gt;mdbook&lt;/code&gt; does use stdin to receive messages and stdout to send them, via serialization. So it’s basically the IPC &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#pipes&quot;&gt;Based on Pipes&lt;/a&gt; approach.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/zellij-org/zellij&quot;&gt;&lt;code&gt;zellij&lt;/code&gt;&lt;/a&gt;: a terminal workspace with “a plugin system allowing one to create plugins in any language that compiles to WebAssembly”.&lt;/p&gt;
&lt;p&gt;This is an extremely valuable resource in case the &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#wasm&quot;&gt;WebAssembly&lt;/a&gt; option is chosen, as it’s very similar to what Tremor needs. One can even subscribe to events in order to simulate the traits in Tremor that currently use &lt;code&gt;async&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;After trying it out and seeing its source code, it seems to work with a &lt;code&gt;wasm&lt;/code&gt; binary that acts as a standalone program, where the communication takes place via stdin and stdout, serializing and deserializing with &lt;a href=&quot;https://serde.rs/&quot;&gt;serde&lt;/a&gt;. &lt;code&gt;zellij&lt;/code&gt; includes a few plugins by default, like the status bar, or a file manager. Very neat architecture!&lt;/p&gt;
&lt;p&gt;Other WebAssmbly-based plugin systems: &lt;a href=&quot;https://book.veloren.net/contributors/developers/codebase-structure.html#plugins&quot;&gt;Veloren&lt;/a&gt;, &lt;a href=&quot;https://github.com/feather-rs/feather/tree/main/quill&quot;&gt;Feather&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/xi-editor/xi-editor&quot;&gt;&lt;code&gt;xi&lt;/code&gt;&lt;/a&gt;: a now abandoned modern text editor. Its plugins, described &lt;a href=&quot;https://raphlinus.github.io/xi/2020/06/27/xi-retrospective.html#json&quot;&gt;here in detail&lt;/a&gt;, are based on &lt;a href=&quot;https://docs.rs/xi-rpc/0.3.0/xi_rpc/&quot;&gt;JSON RPC&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Text editors overall are very interesting, because they must be built with extensibility in mind and thus have to implement some kind of plugin system.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Visual Studio Code is based on Electron, so it has a renderer process for the frontend, and a main process with Node. Its extensions share the same event loop, &lt;a href=&quot;https://github.com/microsoft/vscode/issues/75627#issuecomment-519125065&quot;&gt;which has been an issue for a long time in case an extension blocks temporarily&lt;/a&gt;, causing lag.&lt;/li&gt;
&lt;li&gt;Eclipse’s plugin architecture is based on Java classes&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fn16&quot; id=&quot;fnref16&quot;&gt;[16]&lt;/a&gt;&lt;/sup&gt; loaded at runtime and configured with XML manifests.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://bevyengine.org/&quot;&gt;&lt;code&gt;bevy&lt;/code&gt;&lt;/a&gt;: a very promising game engine whose features are implemented as plugins. Most times they are loaded at compile-time, but the &lt;a href=&quot;https://docs.rs/bevy/0.5.0/bevy/dynamic_plugin/index.html&quot;&gt;&lt;code&gt;bevy::dynamic_plugin&lt;/code&gt;&lt;/a&gt; allows this to happen at runtime. It uses &lt;code&gt;libloading&lt;/code&gt; internally, with actually &lt;a href=&quot;https://github.com/bevyengine/bevy/blob/v0.5.0/crates/bevy_dynamic_plugin/src/loader.rs&quot;&gt;very little code&lt;/a&gt;.&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;a name=&quot;_conclusion&quot;&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/plugin-tech/#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;This article has covered quite a few ways to approach a Plugin System. The final choice depends on what trade-offs the project wants to make. Most of them require sacrificing some level of performance for safety or usability. Here’s a drawing that &lt;em&gt;&lt;strong&gt;very roughly&lt;/strong&gt;&lt;/em&gt; compares the main methods:&lt;/p&gt;
&lt;picture&gt;&lt;source type=&quot;image/avif&quot; srcset=&quot;https://nullderef.com/blog/plugin-tech/HP_jfT2mNl-500.avif 500w&quot;&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://nullderef.com/blog/plugin-tech/HP_jfT2mNl-500.webp 500w&quot;&gt;&lt;img loading=&quot;lazy&quot; decoding=&quot;async&quot; src=&quot;https://nullderef.com/blog/plugin-tech/HP_jfT2mNl-500.jpeg&quot; alt=&quot;Triangle Chart&quot; width=&quot;500&quot; height=&quot;279&quot;&gt;&lt;/picture&gt;
&lt;p&gt;There’s never a single answer in programming: how much performance are you willing to lose in exchange for safety and usability? Is that performance actually measurable, or is it just hypothetical? Don’t forget that this depends on the use case, so make sure you run a couple benchmarks if the resulting overhead may be important for your program.&lt;/p&gt;
&lt;p&gt;You can join the discussion at &lt;a href=&quot;https://www.reddit.com/r/rust/comments/nenql8/plugins_in_rust_the_technologies_nullderefcom/?&quot;&gt;Reddit&lt;/a&gt; if you have any additional suggestions or comments, or leave a comment below.&lt;/p&gt;

&lt;script src=&quot;https://f.convertkit.com/ckjs/ck.5.js&quot;&gt;&lt;/script&gt;
&lt;form action=&quot;https://app.kit.com/forms/7534343/subscriptions&quot; class=&quot;seva-form formkit-form&quot; method=&quot;post&quot; data-sv-form=&quot;7534343&quot; data-uid=&quot;3680381778&quot; data-format=&quot;inline&quot; data-version=&quot;5&quot; data-options=&#39;{&quot;settings&quot;:{&quot;after_subscribe&quot;:{&quot;action&quot;:&quot;message&quot;,&quot;success_message&quot;:&quot;Success! Check your email to confirm your subscription. If you can’t find it, look in the spam folder.&quot;,&quot;redirect_url&quot;:&quot;&quot;},&quot;analytics&quot;:{&quot;google&quot;:null,&quot;fathom&quot;:null,&quot;facebook&quot;:null,&quot;segment&quot;:null,&quot;pinterest&quot;:null,&quot;sparkloop&quot;:null,&quot;googletagmanager&quot;:null},&quot;modal&quot;:{&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;powered_by&quot;:{&quot;show&quot;:true,&quot;url&quot;:&quot;https://kit.com/features/forms?utm_campaign=poweredby&amp;amp;utm_content=form&amp;amp;utm_medium=referral&amp;amp;utm_source=dynamic&quot;},&quot;recaptcha&quot;:{&quot;enabled&quot;:false},&quot;return_visitor&quot;:{&quot;action&quot;:&quot;show&quot;,&quot;custom_content&quot;:&quot;&quot;},&quot;slide_in&quot;:{&quot;display_in&quot;:&quot;bottom_right&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;sticky_bar&quot;:{&quot;display_in&quot;:&quot;top&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15}},&quot;version&quot;:&quot;5&quot;}&#39; min-width=&quot;400 500 600 700 800&quot;&gt;
  &lt;div data-style=&quot;clean&quot;&gt;
    &lt;div class=&quot;formkit-header&quot; data-element=&quot;header&quot;&gt;
      &lt;h2&gt;Want to receive an email when I post?&lt;/h2&gt;
    &lt;/div&gt;
    &lt;ul class=&quot;formkit-alert formkit-alert-error&quot; data-element=&quot;errors&quot; data-group=&quot;alert&quot;&gt;&lt;/ul&gt;
    &lt;div data-element=&quot;fields&quot; data-stacked=&quot;false&quot; class=&quot;seva-fields formkit-fields&quot;&gt;
      &lt;div class=&quot;formkit-field&quot;&gt;
        &lt;input class=&quot;formkit-input&quot; name=&quot;email_address&quot; aria-label=&quot;Email Address&quot; placeholder=&quot;Email Address&quot; required=&quot;&quot; type=&quot;email&quot;&gt;
      &lt;/div&gt;
      &lt;button data-element=&quot;submit&quot; class=&quot;formkit-submit&quot; style=&quot;background-color:#e06c75;font-weight:700&quot;&gt;
        &lt;div class=&quot;formkit-spinner&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span class=&quot;&quot;&gt;Subscribe&lt;/span&gt;
      &lt;/button&gt;
    &lt;/div&gt;
    &lt;div class=&quot;formkit-guarantee&quot; data-element=&quot;guarantee&quot;&gt;
      &lt;p&gt;&lt;em&gt;Free and without spam. You can also use my &lt;a href=&quot;https://nullderef.com/index.xml&quot; target=&quot;_blank&quot;&gt;RSS&lt;/a&gt;, &lt;a href=&quot;https://x.com/null_deref&quot; target=&quot;_blank&quot;&gt;X&lt;/a&gt;, or &lt;a href=&quot;https://mstdn.social/@nullderef&quot; target=&quot;_blank&quot;&gt;Mastodon&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
      &lt;p style=&quot;opacity:.6;font-size:16px&quot;&gt;&lt;em&gt;Powered by &lt;a href=&quot;https://kit.com/&quot; target=&quot;_black&quot;&gt;Kit&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;style&gt;.formkit-form[data-uid=&quot;3680381778&quot;] a{text-decoration:underline}.formkit-form[data-uid=&quot;3680381778&quot;] legend{border:none;font-size:inherit;margin-bottom:10px;padding:0;position:relative;display:table}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-checkboxes,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-select{width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{border:0;border-radius:var(--radius);color:#fff;cursor:pointer;display:inline-block;text-align:center;font-weight:500;cursor:pointer;margin-bottom:15px;overflow:hidden;padding:0;position:relative;vertical-align:middle}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover{outline:0}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover&gt;span{background-color:rgba(0,0,0,.1)}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit&gt;span{display:block;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out;padding:12px 24px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input{background:#fff;padding:12px;border:1px solid var(--border);border-radius:var(--radius);-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;line-height:1.4;margin:0;-webkit-transition:border-color ease-out .3s;transition:border-color ease-out .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:focus{outline:0;border-color:#1677be;-webkit-transition:border-color ease .3s;transition:border-color ease .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-webkit-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-moz-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:-ms-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert{background:#f9fafb;border:1px solid #e3e3e3;border-radius:5px;-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;list-style:none;margin:25px auto;padding:12px;text-align:center;width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert:empty{display:none}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-success{background:#d3fbeb;border-color:#10bf7a;color:#0c905c}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-error{background:#fde8e2;border-color:#f2643b;color:#ea4110}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;height:0;width:0;margin:0 auto;position:absolute;top:0;left:0;right:0;width:0;overflow:hidden;text-align:center;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div{margin:auto;width:12px;height:12px;background-color:#fff;opacity:.3;border-radius:100%;display:inline-block;-webkit-animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both;animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:first-child{-webkit-animation-delay:-.32s;animation-delay:-.32s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:nth-child(2){-webkit-animation-delay:-.16s;animation-delay:-.16s}.formkit-form[data-uid=&quot;3680381778&quot;]{border:1px solid var(--border);border-radius:var(--radius);position:relative;overflow:hidden;background:var(--code-bg)}.formkit-form[data-uid=&quot;3680381778&quot;]{padding:20px;width:100%;position:relative}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-header{margin:0 0 27px 0;font-size:20px;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee{margin-top:10px;margin-bottom:32px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee&gt;p{margin:0;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-fields{display:flex;flex-wrap:wrap;margin:25px auto 0 auto;gap:10px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-field,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{flex:1 0 100%}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;]{padding:0 30px}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-field{flex:100 1 auto}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-submit{flex:1 1 auto}&lt;/style&gt;
&lt;/form&gt;
&lt;h2 id=&quot;footnotes&quot;&gt;Footnotes&lt;/h2&gt;
&lt;ol class=&quot;footnotes-list&quot;&gt;&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://jamie.build/how-to-build-an-npm-worm&quot;&gt;How to build an npm worm — Jamie Kyle&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn2&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://snyk.io/blog/yet-another-malicious-package-found-in-npm-targeting-cryptocurrency-wallets/&quot;&gt;Yet another malicious package found in npm, targeting cryptocurrency wallets — snyk&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fnref2&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn3&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://gankra.github.io/blah/swift-abi/&quot;&gt;How Swift Achieved Dynamic Linking Where Rust Couldn’t — Alexis Beingessner&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fnref3&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn4&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.lua.org/about.html#why&quot;&gt;About Lua — lua.org&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fnref4&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn5&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;http://lua-users.org/wiki/SandBoxes&quot;&gt;Sand Boxes — lua-users&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fnref5&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn6&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://pages.cs.wisc.edu/~adityav/Evaluation_of_Inter_Process_Communication_Mechanisms.pdf&quot;&gt;Evaluation of Inter-Process Communication Mechanisms (paper)&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fnref6&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn7&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://google.github.io/flatbuffers/flatbuffers_benchmarks.html&quot;&gt;C++ Benchmarks — FlatBuffers&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fnref7&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn8&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://stackoverflow.com/a/14512554/11488352&quot;&gt;Performance difference between IPC shared memory and threads memory — StackOverflow&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fnref8&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn9&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://docs.rs/dlopen/0.1.8/dlopen/#compare-with-other-libraries&quot;&gt;Compare with other libraries — dlopen v0.1.8&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fnref9&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn10&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://stackoverflow.com/a/46249019/11488352&quot;&gt;Idiomatic Rust plugin system — StackOverflow&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fnref10&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn11&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://github.com/kurtlawrence/papyrus/blob/1c7f0a669fed59d220bdefb161c568072126d3d5/src/compile/execute.rs#L36&quot;&gt;&lt;code&gt;papyrus/src/compile/execute.rs&lt;/code&gt; — GitHub kurtlawrence/papyrus&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fnref11&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn12&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://docs.rs/libloading/0.7.0/libloading/struct.Library.html#thread-safety&quot;&gt;Thread-safety — libloading v0.7.0&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fnref12&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn13&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://docs.rs/dlopen/0.1.8/dlopen/#safety&quot;&gt;Safety — dlopen v0.1.8&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fnref13&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn14&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;span class=&quot;gh-reference&quot;&gt;&lt;svg class=&quot;gh-icon&quot; xmlns=&quot;http://www.w3.org/2000/svg&quot; width=&quot;16&quot; height=&quot;16&quot; viewBox=&quot;0 0 16 16&quot;&gt;&lt;path d=&quot;M8 9.5a1.5 1.5 0 100-3 1.5 1.5 0 000 3z&quot;&gt;&lt;/path&gt;&lt;path fill-rule=&quot;evenodd&quot; d=&quot;M8 0a8 8 0 100 16A8 8 0 008 0zM1.5 8a6.5 6.5 0 1113 0 6.5 6.5 0 01-13 0z&quot;&gt;&lt;/path&gt;&lt;/svg&gt; &lt;a class=&quot;gh-title&quot; href=&quot;https://github.com/amethyst/amethyst/issues/1729&quot;&gt;[NEW FEATURE] WebAssembly scripting system &lt;span class=&quot;gh-number&quot;&gt;amethyst/amethyst#1729&lt;/span&gt;&lt;/a&gt;&lt;/span&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fnref14&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn15&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://docs.rs/wasmer/2.0.0/wasmer/type.Val.html&quot;&gt;&lt;code&gt;Val&lt;/code&gt; — Wasmer v2.0.0&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fnref15&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li id=&quot;fn16&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;&lt;a href=&quot;https://www.eclipse.org/articles/Article-Plug-in-architecture/plugin_architecture.html&quot;&gt;Notes on the Eclipse Plug-in Architecture - Eclipse&lt;/a&gt; &lt;a href=&quot;https://nullderef.com/blog/plugin-tech/#fnref16&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;</content>
  </entry>
  <entry>
    <title>Optional parameters in Rust</title>
    <link href="https://nullderef.com/blog/rust-parameters/" />
    <updated>2020-10-10T00:00:00Z</updated>
    <id>https://nullderef.com/blog/rust-parameters/</id>
    <content type="html">&lt;details class=&quot;toc&quot;&gt;
&lt;summary&gt;Table of Contents&lt;/summary&gt;
&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-parameters/#introducing-an-example&quot;&gt;Introducing an example &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-parameters/#a-using-option-t&quot;&gt;A) Using Option&lt;t&gt; &lt;/t&gt;&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-parameters/#upsides&quot;&gt;Upsides &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-parameters/#downsides&quot;&gt;Downsides &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-parameters/#b-with-into-option-t&quot;&gt;B) With Into&amp;lt;Option&lt;t&gt;&amp;gt; &lt;/t&gt;&lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-parameters/#upsides-1&quot;&gt;Upsides &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-parameters/#downsides-1&quot;&gt;Downsides &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-parameters/#c-with-a-custom-struct&quot;&gt;C) With a custom struct &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-parameters/#upsides-2&quot;&gt;Upsides &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-parameters/#downsides-2&quot;&gt;Downsides &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-parameters/#d-with-the-builder-pattern&quot;&gt;D) With the builder pattern &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-parameters/#upsides-3&quot;&gt;Upsides &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-parameters/#downsides-3&quot;&gt;Downsides &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-parameters/#e-endpoint-oriented-interface&quot;&gt;E) Endpoint-oriented interface &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-parameters/#upsides-4&quot;&gt;Upsides &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-parameters/#downsides-4&quot;&gt;Downsides &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-parameters/#f-hybrid-derive-pattern&quot;&gt;F) Hybrid derive pattern &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-parameters/#upsides-5&quot;&gt;Upsides &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-parameters/#downsides-5&quot;&gt;Downsides &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-parameters/#g-h-grouping-up-endpoints&quot;&gt;G,H) Grouping up endpoints &lt;/a&gt;&lt;ul&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-parameters/#upsides-6&quot;&gt;Upsides &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-parameters/#downsides-6&quot;&gt;Downsides &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-parameters/#i-macros&quot;&gt;I) Macros &lt;/a&gt;&lt;/li&gt;&lt;li&gt;&lt;a href=&quot;https://nullderef.com/blog/rust-parameters/#conclusion&quot;&gt;Conclusion &lt;/a&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/details&gt;
&lt;p&gt;Optional or default parameters are a very interesting feature of some languages that Rust specifically doesn’t cover (and looks like it won’t &lt;a href=&quot;https://github.com/rust-lang/rfcs/pull/2964&quot;&gt;anytime soon&lt;/a&gt;). Say your library has lots of endpoints like so:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;endpoint&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;mandatory&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; opt1&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; opt2&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T3&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;...&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this case, when you call &lt;code&gt;endpoint&lt;/code&gt;, you have to use &lt;code&gt;endpoint(mandatory, None, None, ...)&lt;/code&gt;, or &lt;code&gt;endpoint(mandatory, Some(val1), Some(val2), ...)&lt;/code&gt;, instead of the more intuitive &lt;code&gt;endpoint(mandatory)&lt;/code&gt; or &lt;code&gt;endpoint(mandatory, val1, val2)&lt;/code&gt;. Other languages like Python have named arguments, which make optional parameters natural and easier to read: &lt;code&gt;endpoint(mandatory, opt1=val1, opt2=val2)&lt;/code&gt;, while also allowing them to be written in any order.&lt;/p&gt;
&lt;p&gt;Even without official support, there are lots of different ways to approach them in Rust, which is what this blog post tries to analyze. My goal is not to show which one is the “best” option, but to exhaustively showcase the different ways they can be approached, and the ups and downs of each of them.&lt;/p&gt;
&lt;h2 id=&quot;introducing-an-example&quot; tabindex=&quot;-1&quot;&gt;Introducing an example &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-parameters/#introducing-an-example&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Let’s start with a typical web API wrapper library. These often require a client struct to hold authentication fields and such:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[derive(Default, Debug, Clone)]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;APIClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This client will have various endpoints which can be used to send requests to the server. We will use a function with the actual implementation and some type aliases so that the following snippets are easier to read:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;use&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;std&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;error&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/// Some value that the endpoint will return&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#[derive(Debug, Default)]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;ReturnedValue&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Result&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Box&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;dyn&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Error&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/// The actual code for the endpoint inside the client&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;APIClient&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;actual_endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; opt1&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; opt2&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReturnedValue&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token macro property&quot;&gt;println!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;params: {} {:?} {:?}&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; opt1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; opt2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token class-name&quot;&gt;Ok&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2 id=&quot;a-using-option-t&quot; tabindex=&quot;-1&quot;&gt;A) Using &lt;code&gt;Option&amp;lt;T&amp;gt;&lt;/code&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-parameters/#a-using-option-t&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The simplest way to do this would be to just use the &lt;code&gt;actual_endpoint&lt;/code&gt; function signature. Sometimes the best solution is the simplest, and your project just might not need more complex approaches. Do consider if anything more elaborate is actually necessary.&lt;/p&gt;
&lt;p&gt;For the sake of this example, it would look like this:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;APIClient&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;approach_a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; opt1&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; opt2&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReturnedValue&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;actual_endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; opt1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; opt2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; api &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;APIClient&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; param2&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;u32&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;324&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;approach_a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;option&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;param2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1234&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;approach_a&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;option&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;upsides&quot; tabindex=&quot;-1&quot;&gt;Upsides &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-parameters/#upsides&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Simplest to understand and implement.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;downsides&quot; tabindex=&quot;-1&quot;&gt;Downsides &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-parameters/#downsides&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Multiple optional parameters require lots of &lt;code&gt;None&lt;/code&gt; and &lt;code&gt;Some&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Parameter names unknown when reading the code, which is specially annoying with &lt;code&gt;None&lt;/code&gt; values, since these don’t have context to know what they are for.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;b-with-into-option-t&quot; tabindex=&quot;-1&quot;&gt;B) With &lt;code&gt;Into&amp;lt;Option&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt; &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-parameters/#b-with-into-option-t&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;A variation of the previous approach consists on using &lt;code&gt;Into&amp;lt;Option&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt; as the generic value for the optional parameters (&lt;code&gt;impl Into&amp;lt;Option&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt; can be used as well). This way, &lt;code&gt;Some&lt;/code&gt; isn’t needed when the optional parameters are specified:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;APIClient&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;approach_b&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token constant&quot;&gt;T1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T2&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; opt1&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; opt2&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;T2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReturnedValue&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt;
        &lt;span class=&quot;token constant&quot;&gt;T1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Into&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token constant&quot;&gt;T2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Into&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;actual_endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; opt1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;into&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; opt2&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;into&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;approach_b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;into_option&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; param2&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1234&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// This still works&lt;/span&gt;
api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;approach_b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;into_option&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;param2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;approach_b&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;into_option&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;upsides-1&quot; tabindex=&quot;-1&quot;&gt;Upsides &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-parameters/#upsides-1&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;Some&lt;/code&gt; isn’t required, which makes it slightly easier to read.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;downsides-1&quot; tabindex=&quot;-1&quot;&gt;Downsides &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-parameters/#downsides-1&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Multiple optional parameters still require lots of &lt;code&gt;None&lt;/code&gt;s.&lt;/li&gt;
&lt;li&gt;No parameter names either.&lt;/li&gt;
&lt;li&gt;More complex function signatures, and might not be too “idiomatic”.&lt;/li&gt;
&lt;li&gt;Requires generics, 2^N copies of this function may be generated, where N is the number of optional parameters.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;c-with-a-custom-struct&quot; tabindex=&quot;-1&quot;&gt;C) With a custom struct &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-parameters/#c-with-a-custom-struct&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Another option is to create a struct that holds the parameters and use that instead. The complexity is still relatively simple, and it can work out well if the API has functions with repetitive function signatures. This will serve as a base for the following approaches as well:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; call1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;params&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ApproachC&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;builder&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to_string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    opt1&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;param2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    opt2&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;123&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;approach_c&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;call1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We can even use &lt;code&gt;..Default::default()&lt;/code&gt; to initialize the rest of the parameters with their default values:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; call2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;params&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ApproachC&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;builder&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;to_string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;..&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;approach_c&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;call2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The implementation looks like this:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;mod&lt;/span&gt; &lt;span class=&quot;token module-declaration namespace&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// We derive `Default` to be able to initialize with&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// `..Default::default()`.&lt;/span&gt;
    &lt;span class=&quot;token attribute attr-name&quot;&gt;#[derive(Default)]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;ApproachC&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; opt1&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; opt2&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;APIClient&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;approach_c&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;params&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ApproachC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReturnedValue&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;actual_endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;opt1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;opt2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;upsides-2&quot; tabindex=&quot;-1&quot;&gt;Upsides &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-parameters/#upsides-2&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Some APIs might feel more natural this way, if the combination of these parameters as a group makes sense.&lt;/li&gt;
&lt;li&gt;The struct can be reused in different calls.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;downsides-2&quot; tabindex=&quot;-1&quot;&gt;Downsides &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-parameters/#downsides-2&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Multiple optional parameters require lots of &lt;code&gt;None&lt;/code&gt; and &lt;code&gt;Some&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Way more verbose.&lt;/li&gt;
&lt;li&gt;Can be difficult to scale, since it needs a struct and a function per endpoint.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;d-with-the-builder-pattern&quot; tabindex=&quot;-1&quot;&gt;D) With the builder pattern &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-parameters/#d-with-the-builder-pattern&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;The previous approach can be improved by using the &lt;a href=&quot;https://doc.rust-lang.org/1.0.0/style/ownership/builders.html&quot;&gt;builder pattern&lt;/a&gt; for the parameters, so that building the parameters is simpler and more pretty to look at:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; call1 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;params&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ApproachDBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;builder&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;opt1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;param2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;opt2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2134&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;approach_d&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;call1&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; call2 &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;params&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ApproachDBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;builder&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;approach_d&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;call2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this case, we use the &lt;a href=&quot;https://crates.io/crates/derive_builder&quot;&gt;&lt;code&gt;derive_builder&lt;/code&gt;&lt;/a&gt; crate to make the implementation less repetitive:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;mod&lt;/span&gt; &lt;span class=&quot;token module-declaration namespace&quot;&gt;params&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute attr-name&quot;&gt;#[derive(Default, Builder)]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;ApproachD&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token attribute attr-name&quot;&gt;#[builder(setter(into))]&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token attribute attr-name&quot;&gt;#[builder(setter(strip_option), default)]&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; opt1&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token attribute attr-name&quot;&gt;#[builder(setter(strip_option), default)]&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; opt2&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;APIClient&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;approach_d&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token namespace&quot;&gt;params&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;ApproachD&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReturnedValue&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;actual_endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;opt1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;opt2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3 id=&quot;upsides-3&quot; tabindex=&quot;-1&quot;&gt;Upsides &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-parameters/#upsides-3&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;It’s the most popular pattern in Rust for optional parameters.&lt;/li&gt;
&lt;li&gt;The struct can still be reused in different calls.&lt;/li&gt;
&lt;li&gt;No &lt;code&gt;None&lt;/code&gt; or &lt;code&gt;Some&lt;/code&gt; at sight.&lt;/li&gt;
&lt;li&gt;Optional parameters are now associated to their name, which makes it easier to read.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;downsides-3&quot; tabindex=&quot;-1&quot;&gt;Downsides &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-parameters/#downsides-3&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Somewhat verbose.&lt;/li&gt;
&lt;li&gt;In our example it can be difficult to scale, since it needs a struct and a function per endpoint.&lt;/li&gt;
&lt;li&gt;More overhead, both at runtime and compile-time (specially if macros are used).&lt;/li&gt;
&lt;li&gt;Constructing the values may fail. Thus, the documentation has to specify very clearly which parameters are mandatory and which aren’t.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;e-endpoint-oriented-interface&quot; tabindex=&quot;-1&quot;&gt;E) Endpoint-oriented interface &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-parameters/#e-endpoint-oriented-interface&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Here’s a different take: what if the API was endpoint-oriented instead of client-oriented? Starting from the previous approach, we could make the call &lt;em&gt;inside the endpoint struct itself&lt;/em&gt; instead of in the API’s client by adding it to &lt;code&gt;ApproachE&lt;/code&gt;, or by overriding the &lt;code&gt;build&lt;/code&gt; method from &lt;code&gt;derive_builder&lt;/code&gt; for a slightly less verbose version:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;ApproachEBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;endpoint-oriented&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;opt1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;param2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;opt2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1111&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;api&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;ApproachEBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;endpoint-oriented&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;api&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And its implementation:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token attribute attr-name&quot;&gt;#[derive(Default, Builder)]&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#[builder(build_fn(private))]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;ApproachE&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute attr-name&quot;&gt;#[builder(setter(into))]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token attribute attr-name&quot;&gt;#[builder(setter(strip_option), default)]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; opt1&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token attribute attr-name&quot;&gt;#[builder(setter(strip_option), default)]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; opt2&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ApproachEBuilder&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; client&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;APIClient&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReturnedValue&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// This might fail!&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// `actual_endpoint` would have to be at least pub(in crate) in this&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// case.&lt;/span&gt;
        client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;actual_endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;opt1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;opt2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note: this is assuming the client contains necessary information to make the requests, like a &lt;a href=&quot;https://docs.rs/reqwest/latest/reqwest/struct.Client.html&quot;&gt;&lt;code&gt;reqwest::Client&lt;/code&gt;&lt;/a&gt; or authentication details. But for example, &lt;code&gt;ureq&lt;/code&gt; can perform calls without a client instance, just by calling &lt;a href=&quot;https://docs.rs/ureq/latest/ureq/fn.get.html&quot;&gt;&lt;code&gt;ureq::get&lt;/code&gt; and similars&lt;/a&gt;. In that case, the API could just not have a client at all, and the &lt;code&gt;call&lt;/code&gt; method wouldn’t require a reference to the client.&lt;/p&gt;
&lt;h3 id=&quot;upsides-4&quot; tabindex=&quot;-1&quot;&gt;Upsides &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-parameters/#upsides-4&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Fits perfectly for some specific APIs.&lt;/li&gt;
&lt;li&gt;No &lt;code&gt;None&lt;/code&gt; or &lt;code&gt;Some&lt;/code&gt; needed.&lt;/li&gt;
&lt;li&gt;Just as readable as the previous case.&lt;/li&gt;
&lt;li&gt;The struct may be reused.&lt;/li&gt;
&lt;li&gt;Simple to use and implement, since it doesn’t need declaring both a function in the client and a parameters struct, only the latter.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;downsides-4&quot; tabindex=&quot;-1&quot;&gt;Downsides &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-parameters/#downsides-4&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Still relatively verbose, might not be compatible with some APIs.&lt;/li&gt;
&lt;li&gt;Slight builder pattern overhead, and can also fail to construct the value.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;f-hybrid-derive-pattern&quot; tabindex=&quot;-1&quot;&gt;F) Hybrid derive pattern &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-parameters/#f-hybrid-derive-pattern&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Back to the client-oriented API. We can remove some disadvantages of the builder pattern by using a different approach and possibly a custom implementation instead of just using &lt;code&gt;derive_builder&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The client will now have a method that calls &lt;code&gt;ApproachBuilder::default()&lt;/code&gt; and whatever is necessary to start building the endpoint. Mandatory parameters are added to that function’s signature, so that the build can never fail. This also avoids passing the client in &lt;code&gt;call(&amp;amp;api)&lt;/code&gt;, since we can do that inside the method. This is how it would look like:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;approach_f&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;hybrid-derive-pattern&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;opt1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;param2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;opt2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2222&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And an implementation by wrapping &lt;code&gt;derive_builder&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;APIClient&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;approach_f&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ApproachFBuilder&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;ApproachFBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;/// Not meant to be used directly, only within `APIClient`.&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#[derive(Default, Builder)]&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#[builder(build_fn(private), pattern = &lt;span class=&quot;token string&quot;&gt;&quot;owned&quot;&lt;/span&gt;)]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;ApproachF&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute attr-name&quot;&gt;#[builder(setter(strip_option), default)]&lt;/span&gt;
    client&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;APIClient&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token attribute attr-name&quot;&gt;#[builder(setter(into))]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token attribute attr-name&quot;&gt;#[builder(setter(strip_option), default)]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; opt1&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token attribute attr-name&quot;&gt;#[builder(setter(strip_option), default)]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; opt2&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ApproachFBuilder&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;_&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;call&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReturnedValue&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;// This should never fail&lt;/span&gt;
        data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;client
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;actual_endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;opt1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;opt2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;derive_builder&lt;/code&gt; doesn’t know that &lt;code&gt;build()&lt;/code&gt; can never technically fail because the mandatory parameters are provided inside the wrapper, so it’s kind of a waste to use this macro. It also forces us to create the wrapper because the builder always has to be initialized with &lt;code&gt;Builder::default()&lt;/code&gt;. With a custom implementation, we could have this new method integrated in the builder itself instead, like &lt;code&gt;Builder::new(A, B, C)&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Thus, it might be better to use other crates like &lt;a href=&quot;https://crates.io/crates/typed-builder&quot;&gt;&lt;code&gt;typed-builder&lt;/code&gt;&lt;/a&gt;, or just a custom implementation.&lt;/p&gt;
&lt;h3 id=&quot;upsides-5&quot; tabindex=&quot;-1&quot;&gt;Upsides &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-parameters/#upsides-5&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Although mandatory parameters don’t have a parameter name now, it’s still quite readable. It’s also much less verbose.&lt;/li&gt;
&lt;li&gt;No &lt;code&gt;None&lt;/code&gt; or &lt;code&gt;Some&lt;/code&gt; needed.&lt;/li&gt;
&lt;li&gt;Can’t fail to initialize the endpoint value, so it’s “safer”.&lt;/li&gt;
&lt;li&gt;A custom implementation would avoid runtime overhead.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;downsides-5&quot; tabindex=&quot;-1&quot;&gt;Downsides &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-parameters/#downsides-5&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Currently a bit hacky to implement, which makes it much more complex, specially because there’s no existing macro that can simplify this specific variation of the builder pattern (that I know of). It requires both a function and a struct as well, so the implementation can be quite lengthy.&lt;/li&gt;
&lt;li&gt;Still some compilation-time overhead.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;g-h-grouping-up-endpoints&quot; tabindex=&quot;-1&quot;&gt;G,H) Grouping up endpoints &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-parameters/#g-h-grouping-up-endpoints&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Another possible approach based on the previous approach of the builder pattern consists on grouping up all or some of the endpoints under a single struct. They will share the optional parameters, which is useful to avoid declaring a struct with optional parameters for each endpoint we have, and makes a lot of sense for some APIs.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;call&lt;/code&gt; method could be removed in place of &lt;code&gt;approach_f&lt;/code&gt;, and the optional parameters would go first. This would require a different order for the optional parameters. Here’s an example if all the endpoints shared the same optional parameters:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;opt1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;param2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;opt2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2222&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;approach_f&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;group-builder-pattern&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And if we used different groups it would look like this:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;opt1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;param2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;opt2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2222&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;approach_g&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;group-builder-pattern&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
api&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;opt2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;2222&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;approach_h&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;builder-from-scratch&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here’s an implementation with &lt;code&gt;derive_builder&lt;/code&gt;:&lt;/p&gt;
&lt;pre class=&quot;language-rust&quot;&gt;&lt;code class=&quot;language-rust&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;APIClient&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;group&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GroupBuilder&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;GroupBuilder&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;::&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;default&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;client&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token attribute attr-name&quot;&gt;#[derive(Default, Builder)]&lt;/span&gt;
&lt;span class=&quot;token attribute attr-name&quot;&gt;#[builder(build_fn(private), pattern = &lt;span class=&quot;token string&quot;&gt;&quot;owned&quot;&lt;/span&gt;)]&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;struct&lt;/span&gt; &lt;span class=&quot;token type-definition class-name&quot;&gt;Group&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token attribute attr-name&quot;&gt;#[builder(setter(strip_option), default)]&lt;/span&gt;
    client&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;a&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;APIClient&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token attribute attr-name&quot;&gt;#[builder(setter(strip_option), default)]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; opt1&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;u32&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token attribute attr-name&quot;&gt;#[builder(setter(strip_option), default)]&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; opt2&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Option&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;i32&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;impl&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;GroupBuilder&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token lifetime-annotation symbol&quot;&gt;&#39;_&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;approach_g&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReturnedValue&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;client
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;actual_endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;opt1&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;opt2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;pub&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;fn&lt;/span&gt; &lt;span class=&quot;token function-definition function&quot;&gt;approach_h&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; name&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;-&gt;&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;ReturnedValue&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; data &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;build&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

        &lt;span class=&quot;token comment&quot;&gt;// This endpoint doesn&#39;t need `opt1`. It can either be ignored, or&lt;/span&gt;
        &lt;span class=&quot;token comment&quot;&gt;// be an error. I hate silent errors, though.&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;opt1&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;is_some&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token macro property&quot;&gt;panic!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;opt1 isn&#39;t needed for enpoint_h&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;client&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;unwrap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;actual_endpoint&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;name&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;None&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; data&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;opt2&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As the &lt;code&gt;approach_h&lt;/code&gt; endpoint indicates, the shared optional parameters don’t actually have to be strictly the same. As long as they are related, they can share the same group, and some extra verifications can be added in the final call to make sure the user is using it properly.&lt;/p&gt;
&lt;h3 id=&quot;upsides-6&quot; tabindex=&quot;-1&quot;&gt;Upsides &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-parameters/#upsides-6&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;Basically the same as the hybrid builder pattern, but with an easier implementation, and it might fit perfectly for some APIs that have clearly established groups of endpoints.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&quot;downsides-6&quot; tabindex=&quot;-1&quot;&gt;Downsides &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-parameters/#downsides-6&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;It’s a bit odd, specially because the order is inverse to what you’d expect. And as it’s based on the hybrid builder pattern, it may still be hacky to implement and require compilation-time overhead.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2 id=&quot;i-macros&quot; tabindex=&quot;-1&quot;&gt;I) Macros &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-parameters/#i-macros&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Rust macros support variadic arguments, which make it possible to create a macro with named parameters, like &lt;code&gt;foo!(1, c = 30, b = -2.0)&lt;/code&gt;. Ideally, we want a macro that generates the macros for us, which does sound crazy. I wanted to at least try how existing crates approached this, and only found &lt;a href=&quot;https://crates.io/crates/named&quot;&gt;&lt;code&gt;named&lt;/code&gt;&lt;/a&gt; and &lt;a href=&quot;https://crates.io/crates/duang&quot;&gt;&lt;code&gt;duang&lt;/code&gt;&lt;/a&gt;, which haven’t been updated in years, and probably for good. I tried &lt;code&gt;duang&lt;/code&gt; with Rust 1.47 but got some unexpected errors, so we can assume there are no crates that support this yet. It definitely sounds like a fun challenge, if it’s still possible to implement.&lt;/p&gt;
&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;Conclusion &lt;a class=&quot;header-anchor&quot; href=&quot;https://nullderef.com/blog/rust-parameters/#conclusion&quot; aria-hidden=&quot;true&quot;&gt;#&lt;/a&gt;&lt;/h2&gt;
&lt;p&gt;Whew! That took more than I expected. Some of these endpoints might be unnecessarily complicated or straight up weird. But I hope this was as a good showcase of the different ways optional parameters can be approached in Rust, and that reading this served as a learning experience. I look forward to seeing new crates in the future that simplify these approaches.&lt;/p&gt;
&lt;p&gt;The code for the different approaches can be found &lt;a href=&quot;https://github.com/marioortizmanero/rust-optional-params&quot;&gt;here&lt;/a&gt;. Bear in mind that there are a lot of different ways to implement the approaches, as I explained in this post. You can discuss it at the &lt;a href=&quot;https://www.reddit.com/r/rust/comments/j8p6fx/optional_parameters_in_rust&quot;&gt;reddit thread&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Disclaimer: this post was originally in
&lt;a href=&quot;https://vidify.org/blog/rust-parameters/&quot;&gt;https://vidify.org/blog/rust-parameters/&lt;/a&gt;. I’ve moved it to my personal blog.&lt;/em&gt;&lt;/p&gt;

&lt;script src=&quot;https://f.convertkit.com/ckjs/ck.5.js&quot;&gt;&lt;/script&gt;
&lt;form action=&quot;https://app.kit.com/forms/7534343/subscriptions&quot; class=&quot;seva-form formkit-form&quot; method=&quot;post&quot; data-sv-form=&quot;7534343&quot; data-uid=&quot;3680381778&quot; data-format=&quot;inline&quot; data-version=&quot;5&quot; data-options=&#39;{&quot;settings&quot;:{&quot;after_subscribe&quot;:{&quot;action&quot;:&quot;message&quot;,&quot;success_message&quot;:&quot;Success! Check your email to confirm your subscription. If you can’t find it, look in the spam folder.&quot;,&quot;redirect_url&quot;:&quot;&quot;},&quot;analytics&quot;:{&quot;google&quot;:null,&quot;fathom&quot;:null,&quot;facebook&quot;:null,&quot;segment&quot;:null,&quot;pinterest&quot;:null,&quot;sparkloop&quot;:null,&quot;googletagmanager&quot;:null},&quot;modal&quot;:{&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;powered_by&quot;:{&quot;show&quot;:true,&quot;url&quot;:&quot;https://kit.com/features/forms?utm_campaign=poweredby&amp;amp;utm_content=form&amp;amp;utm_medium=referral&amp;amp;utm_source=dynamic&quot;},&quot;recaptcha&quot;:{&quot;enabled&quot;:false},&quot;return_visitor&quot;:{&quot;action&quot;:&quot;show&quot;,&quot;custom_content&quot;:&quot;&quot;},&quot;slide_in&quot;:{&quot;display_in&quot;:&quot;bottom_right&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15},&quot;sticky_bar&quot;:{&quot;display_in&quot;:&quot;top&quot;,&quot;trigger&quot;:&quot;timer&quot;,&quot;scroll_percentage&quot;:null,&quot;timer&quot;:5,&quot;devices&quot;:&quot;all&quot;,&quot;show_once_every&quot;:15}},&quot;version&quot;:&quot;5&quot;}&#39; min-width=&quot;400 500 600 700 800&quot;&gt;
  &lt;div data-style=&quot;clean&quot;&gt;
    &lt;div class=&quot;formkit-header&quot; data-element=&quot;header&quot;&gt;
      &lt;h2&gt;Want to receive an email when I post?&lt;/h2&gt;
    &lt;/div&gt;
    &lt;ul class=&quot;formkit-alert formkit-alert-error&quot; data-element=&quot;errors&quot; data-group=&quot;alert&quot;&gt;&lt;/ul&gt;
    &lt;div data-element=&quot;fields&quot; data-stacked=&quot;false&quot; class=&quot;seva-fields formkit-fields&quot;&gt;
      &lt;div class=&quot;formkit-field&quot;&gt;
        &lt;input class=&quot;formkit-input&quot; name=&quot;email_address&quot; aria-label=&quot;Email Address&quot; placeholder=&quot;Email Address&quot; required=&quot;&quot; type=&quot;email&quot;&gt;
      &lt;/div&gt;
      &lt;button data-element=&quot;submit&quot; class=&quot;formkit-submit&quot; style=&quot;background-color:#e06c75;font-weight:700&quot;&gt;
        &lt;div class=&quot;formkit-spinner&quot;&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;div&gt;&lt;/div&gt;&lt;/div&gt;&lt;span class=&quot;&quot;&gt;Subscribe&lt;/span&gt;
      &lt;/button&gt;
    &lt;/div&gt;
    &lt;div class=&quot;formkit-guarantee&quot; data-element=&quot;guarantee&quot;&gt;
      &lt;p&gt;&lt;em&gt;Free and without spam. You can also use my &lt;a href=&quot;https://nullderef.com/index.xml&quot; target=&quot;_blank&quot;&gt;RSS&lt;/a&gt;, &lt;a href=&quot;https://x.com/null_deref&quot; target=&quot;_blank&quot;&gt;X&lt;/a&gt;, or &lt;a href=&quot;https://mstdn.social/@nullderef&quot; target=&quot;_blank&quot;&gt;Mastodon&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
      &lt;p style=&quot;opacity:.6;font-size:16px&quot;&gt;&lt;em&gt;Powered by &lt;a href=&quot;https://kit.com/&quot; target=&quot;_black&quot;&gt;Kit&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;
    &lt;/div&gt;
  &lt;/div&gt;
  &lt;style&gt;.formkit-form[data-uid=&quot;3680381778&quot;] a{text-decoration:underline}.formkit-form[data-uid=&quot;3680381778&quot;] legend{border:none;font-size:inherit;margin-bottom:10px;padding:0;position:relative;display:table}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-checkboxes,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-select{width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{border:0;border-radius:var(--radius);color:#fff;cursor:pointer;display:inline-block;text-align:center;font-weight:500;cursor:pointer;margin-bottom:15px;overflow:hidden;padding:0;position:relative;vertical-align:middle}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover{outline:0}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button:hover&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:focus&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit:hover&gt;span{background-color:rgba(0,0,0,.1)}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-button&gt;span,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit&gt;span{display:block;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out;padding:12px 24px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input{background:#fff;padding:12px;border:1px solid var(--border);border-radius:var(--radius);-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;line-height:1.4;margin:0;-webkit-transition:border-color ease-out .3s;transition:border-color ease-out .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:focus{outline:0;border-color:#1677be;-webkit-transition:border-color ease .3s;transition:border-color ease .3s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-webkit-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::-moz-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input:-ms-input-placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-input::placeholder{color:inherit;opacity:.8}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert{background:#f9fafb;border:1px solid #e3e3e3;border-radius:5px;-webkit-flex:1 0 auto;-ms-flex:1 0 auto;flex:1 0 auto;list-style:none;margin:25px auto;padding:12px;text-align:center;width:100%}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert:empty{display:none}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-success{background:#d3fbeb;border-color:#10bf7a;color:#0c905c}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-alert-error{background:#fde8e2;border-color:#f2643b;color:#ea4110}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;height:0;width:0;margin:0 auto;position:absolute;top:0;left:0;right:0;width:0;overflow:hidden;text-align:center;-webkit-transition:all .3s ease-in-out;transition:all .3s ease-in-out}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div{margin:auto;width:12px;height:12px;background-color:#fff;opacity:.3;border-radius:100%;display:inline-block;-webkit-animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both;animation:formkit-bouncedelay-formkit-form-data-uid-3680381778- 1.4s infinite ease-in-out both}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:first-child{-webkit-animation-delay:-.32s;animation-delay:-.32s}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-spinner&gt;div:nth-child(2){-webkit-animation-delay:-.16s;animation-delay:-.16s}.formkit-form[data-uid=&quot;3680381778&quot;]{border:1px solid var(--border);border-radius:var(--radius);position:relative;overflow:hidden;background:var(--code-bg)}.formkit-form[data-uid=&quot;3680381778&quot;]{padding:20px;width:100%;position:relative}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-header{margin:0 0 27px 0;font-size:20px;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee{margin-top:10px;margin-bottom:32px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-guarantee&gt;p{margin:0;text-align:center}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-fields{display:flex;flex-wrap:wrap;margin:25px auto 0 auto;gap:10px}.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-field,.formkit-form[data-uid=&quot;3680381778&quot;] .formkit-submit{flex:1 0 100%}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;]{padding:0 30px}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-field{flex:100 1 auto}.formkit-form[data-uid=&quot;3680381778&quot;][min-width~=&quot;600&quot;] .formkit-fields .formkit-submit{flex:1 1 auto}&lt;/style&gt;
&lt;/form&gt;
</content>
  </entry>
</feed>