Jekyll2023-06-15T22:26:03+00:00https://yaah.dev/feed.xmlNew to Me ThingsPoorly written blog posts chronicling my attempts to learn new things.Santiago Pastorino: Maintainer Retention2023-06-15T00:00:00+00:002023-06-15T00:00:00+00:00https://yaah.dev/santiago-maintainer-retention<h1 id="santiago-pastorino-maintainer-retention">Santiago Pastorino: Maintainer Retention</h1>
<p><em>I’ve decided to start a series of listening sessions on rust governance to help build awareness and a shared understanding of our problems and ideas for fixing them. This post represents the first in a series of interviews I plan to give. I plan to record them, though I leave that to the discretion of my interviewees.</em></p>
<p>Let’s begin with some formal introductions. My name is Jane Losare-Lusby. I am a California-based principal software engineer employed by Futurewei Technologies to contribute to the Rust project full-time. I joined the Rust project in late 2019, first working on Clippy before moving on to error handling and the libs team, and most recently contributing to the development of the rust-lang governance RFC as a member of the governance working group. I’m currently an active member of the Style Team and one of five project directors representing the Rust Project on the board of directors of the Rust Foundation, with my specific focus being the area of collaboration<sup id="fnref:1" role="doc-noteref"><a href="#fn:1" class="footnote" rel="footnote">1</a></sup>. Uruguayan software engineer Santiago Pastorino is today’s interviewee. He joined the Rust project around 2017 and is today a compiler team contributor, co-leader of the rustc-dev-guide working group, co-founder of WyeWorks, and RustLatam conference organizer. Futurewei also provides funding for Santiago to maintain the Rust compiler.</p>
<p>I just finished conversing with Santiago Pastorino on Rust governance, what changes we need to make the project healthier, what should be the top priority for the council’s backlog, and other thoughts on Santiago’s mind. First and foremost, we talked about Santiago’s views on the council’s most pressing problem, which needs immediate attention. According to Santiago, retaining project participants instead of alienating or driving away our most valuable resource—our people—is the most pressing issue we must address. We’re failing in this area in various ways, so we must approach this from multiple perspectives.</p>
<p>The first aspect we discussed was accountability in leadership, what it should look like, and how to apply it consistently. Santiago emphasized the importance of making room for people to make mistakes and grow in leadership positions and the need for consequences when people are not learning from their mistakes but as the very last option that we would do our best to avoid. Not being clear about when consequences become more severe can have a chilling effect on people participating in leadership when it creates this feeling that you aren’t allowed to make mistakes, which doesn’t feel realistic; nobody is perfect. We need to create a supportive environment where so long as people are learning and accountable that they feel safe and like they have the opportunity to grow into their roles. We discussed the meaning of accountability vs. responsibility and how accountability means being answerable to others. It’s about transparency when project members in positions of trust make mistakes, so people can make sure we’re learning the proper lessons from them and treating those mistakes with the appropriate gravity they deserve. We talked about the importance of safeguards and having well-defined processes so that there is clarity and less opportunity for people to make mistakes in ways that cause harm.</p>
<p>We also talked briefly about other contributing factors, such as burnout and conflict, though we did not dig into these in detail. We generally acknowledged that many contributing factors need to be accounted for and worked on separately to improve retention.</p>
<p>We discussed how, when it came to accountability, for good or bad, we weren’t consistent in that some of the people involved came forward, but not all of them. Therefore, it is evident that members of project leadership made individual decisions based on each person’s feelings rather than discussing accountability as a group and applying it consistently. We discussed his personal experience of when he witnessed leadership follow a similar pattern, rejecting a particular initiative because they wanted to carry out the task themselves in a larger and more official capacity. He discussed how this was disempowering for the contributors who were eager to lead the initiative. Their objections were based more on personal preference than constructive criticism of the proposal’s shortcomings. The cost of ending these initiatives goes beyond the work that is lost; it also includes the emotional toll it takes on the contributors who are told no, how it alienates them, how it makes them feel untrustworthy and unqualified to lead such a significant initiative, and how it adds to that feeling of alienation that causes maintainers to burn out and leave the project, which feeds back into the more substantial issue.</p>
<p>Next, we discussed difficulties around how people get into leadership positions in the first place and the need for clarity on how to end up in such a role. He noted how, in the Rust project, we have a culture of respect based on technical skills. We prefer to elevate those with the most technical skills to leadership positions, but this doesn’t make much sense since technical skills and leadership skills have little overlap. We discussed strategies and challenges in filling these roles and how, when an organization is less well structured and established, it’s important to promote people from within who are already familiar with the culture and needs of the project so they have a chance of being able to be effective. Long term, however, there are scaling issues here, and it’s essential to get to the point where you can onboard people who don’t have prior experience or connections and still have them be effective. Santiago feels it is necessary to have clearly defined roles and processes for people coming in from the outside to successfully navigate and contribute to an organization.</p>
<p>We also discussed how challenging it can be for an open-source project to attract people to fill these roles and the need for creative solutions to bring people in from backgrounds other than software engineering. I suggested paying people as a good starting point, and Santiago agreed. However, he noted how this introduces the risk of companies funding people to work on Rust, exercising undue influence over the project, and only accounting for their needs. The idea is that if the people hired are only there because it’s their day job, they may end up rubber stamping things, and if there are too many of them in one team, it might cause proposals to go through that don’t correctly account for the needs of all of our users. He emphasized that it’s still best to pay people, but we need to acknowledge and account for this problem explicitly, and he wasn’t sure of the best way to deal with it. I suggested that we have a few tools in place already that we can use to resolve this, a combination of affiliation limits for teams, the consent decision-making process and its focus on objections, and open processes to maintain an awareness of users’ needs. The idea here is that even if you have a majority of people on a team just essentially rubberstamping because of pressure from their employer, all you need is one person listening to the needs of the users and escalating their needs as objections to the proposal, so long as everyone holds each other to the established process. Even then, this scenario isn’t super likely so long as we have affiliation limits since you’d never have a majority of people on any team from one organization. I also emphasized how it’s important to remember this risk in the context of these policies so we don’t forget their motivations and introduce processes like majority voting or overruling objections, which make it easier to ignore minority needs and exacerbate the risk of undue influence.</p>
<p>Next, we discussed the keynote selection process and how it ties into the bigger retention problem. Santiago lacks clarity on why leadership needs a say in the keynote since we already have a program committee of project and community members that effectively represent the project and select all other talks. He is open to the possibility that there are valid reasons he doesn’t know, which he would agree with. Still, he worries that this is just another case of leadership not empowering others to make decisions and communicating a feeling of distrust that further alienates people within and around the project.</p>
<p>Finally, the last thing we discussed on this topic was the communication between leadership and the rest of the teams. How our team members were learning about the keynote fiasco and the context simultaneously as the rest of the community, and how that can contribute to feelings of alienation and being unvalued. He asked, “Is it okay that teams only learn about these problems when the rest of the internet does?” and generally expressed the sentiment that he thinks the answer is no; it’s not okay. He was unsure where to draw the line, who to keep informed and who not to, and how time pressures can play a role. He suggested that not keeping our project members informed contributed to the confusion since many were left to run to social media with their conjecture, creating more chaos and that if we had told them, they could have helped calm the situation while leadership focused on public statements of accountability. He suggested a potential method of propagating this information through the leads and representatives that link teams to the council and how, in the future, next time something like this happens, it should be the responsibility of those inter-team links to call emergency meetings to keep their members informed and answer questions, and how this would contribute to a sense of connection within teams and of being valued.</p>
<p>In our discussion, Santiago shared his belief that maintainer retention is the biggest issue the council should focus on. That the problems with retention stem from insufficient, inconsistent, and unclear accountability in leadership, a lack of trust in project members from leadership, filling leadership roles based on technical skill rather than leadership skill, and a lack of clarity about how to get involved and contribute to the project in a leadership capacity.</p>
<p>These initial interviews focus on problems because the first step in solving these problems is building consensus on what they are. Solving these generally acknowledged problems can then serve as the shared goals that unite and direct our collaboration, making it easier to work together and avoid misunderstandings than if we jumped straight into proposing solutions, leaving others to guess at our motivations. My hope is that the most effective way to work towards solving these problems is to rebuild trust starting with a foundation of shared goals and shared understanding and that these interviews and the shared goals they establish will be an effective strategy for us to invest in trust and connection with each other.</p>
<div class="footnotes" role="doc-endnotes">
<ol>
<li id="fn:1" role="doc-endnote">
<p>https://foundation.rust-lang.org/about/ <a href="#fnref:1" class="reversefootnote" role="doc-backlink">↩</a></p>
</li>
</ol>
</div>Santiago Pastorino: Maintainer RetentionRustConf 2020 CFP Proposal2022-05-07T12:00:00+00:002022-05-07T12:00:00+00:00https://yaah.dev/error-handling-talk-proposal<h1 id="rustconf-2020-cfp-proposal">Rustconf 2020 CFP Proposal</h1>
<p>I figured seeing examples of proposals might help with people who are putting
together proposals for this year’s RustConf, so here’s the proposal from my
accepted rustconf 2020 talk:</p>
<h2 id="error-handling-isnt-all-about-errors">Error Handling Isn’t All About Errors</h2>
<p><strong>Abstract</strong></p>
<p>Error handling in rust is incredibly diverse and constantly evolving. As such
it is often a source of confusion for new and experienced rustaceans alike.
This talk aims to clarify what error handling in rust means, the patterns that
exist, when to apply them, and what libraries currently exist to help with the
task.</p>
<h3 id="for-review-committee">For Review Committee</h3>
<p><strong>Details</strong></p>
<p><strong>Outline</strong>: (30 mins)</p>
<ul>
<li>Brief intro of myself and the problem space (2 minutes)</li>
<li>Touch on error handling primatives in the language (5 minutes)
<ul>
<li>Result</li>
<li>Panic</li>
<li>Try / ?</li>
</ul>
</li>
<li>Deeper dive on how these pieces interact and definitions of terms that are currently rather fuzzy (8 mins)
<ul>
<li>What exactly is an “Error”?</li>
<li>Talk about idioms that exist and common needs that are encountered (4 mins)
<ul>
<li>Defining errors
<ul>
<li>common constraints
<ul>
<li>size of the error</li>
<li>dyn allocations</li>
<li>ease of use</li>
</ul>
</li>
</ul>
</li>
<li>Chaining errors</li>
</ul>
</li>
<li>Seperate what is an Error and what is Context about an error
<ul>
<li>Introduce Backtrace as the canonical example of context about an error</li>
</ul>
</li>
</ul>
</li>
<li>Introduce the error trait and how it addresses the previous concerns Error Trait (3 mins)
<ul>
<li>Mention some shortcomings, specifically aggregating errors, and its lack
of support for context other than Backtrace</li>
</ul>
</li>
<li>Introduce the concept of an Error Reporter (3 mins)</li>
<li>Tie it all together and discuss libraries that exist to help (4 mins)
<ul>
<li>Error defining libraries: thiserror, snafu, adhocerr, etc</li>
<li>Error handling + reporting libraries: anyhow, eyre</li>
<li>Libraries that fit both: failure</li>
<li>Error utility libraries: fehler, tracing-error, errtools</li>
</ul>
</li>
<li>Conclusion and Questions (5 mins)</li>
</ul>
<p><strong>Intended audience:</strong></p>
<p>All rustaceans, for beginners it should help clear up confusion and introduce
them to best pratices, and for experienced users it should bring them up to
date on the best practices that have evolved in recent years.</p>
<p><strong>Outcomes:</strong></p>
<p>Hopefully this talk will help crystalize the distinctions between context,
errors, and error reporters. My belief is that the lack of these distinctions
is one of the biggest sources of confusion in the error handling ecosystem, and
that if these distinctions become widely known error handling in rust will no
longer be a major roadblock to learning rust. I’d also like to see library APIs
updated in the future to better reflect these distinctions and hopefully some
RFCs for the error trait to fix the shortcomings I’ve hightlighted.</p>
<p><strong>Pitch</strong></p>
<p>Error handling in rust is a topic that ends up confusing new rustaceans and
experts alike. Part of the challenge is that error handling in rust is quite
flexible and the needs of the ecosystem for error handling are incredibly
diverse. Along side this, the error handling patterns and best practices in the
ecosystem are still actively evolving and crystalizing into clear abstractions,
which can make it unsurprisingly difficult to find information that that is
relevant to your needs and up to date with the latest and greatest patterns
while also making it easy to fall behind.</p>
<p>This talk aims to serve as a primer on the state of error handling in rust as
of 2020, the patterns that exist, the best pratices that have evolved but
aren’t yet widely known, and the libraries that exist to help implement the
error handling system that best fits your project.</p>
<p>This talk aims to make error handling much less confusing by introducing 3 core
abstractions where there is currently one. Specifically it will introduce
Context and Error Reporters and clarify how these differ from Errors. The hope
is that with these three ideas in mind it is much easier to infer what building
blocks you need based on your problem domain.</p>
<p>This talk will focus on runtime errors, and the types that one would put in the
Err variant of a Result, but it will also touch on Result, the <code class="language-plaintext highlighter-rouge">?</code> operator,
the try trait, and other various fundamental building blocks of error handling.
It will briefly discuss panics and their relation to Error types. It will
attempt clearly define what an error is and separate this from context about
errors. It will also introduce a new concept of an Error Reporter, and
differentiate this from both Errors and Context.</p>
<p>The goal is that people can use this talk as a comprehensive guide on error
handling. It will be able to serve as a glossery of definitions of various
terms related to Error handling. It will list as many error handling idioms as
it reasonably can, and the various actively maintained and widely applicable
error handling libraries that currently exist. It should be useful to both
beginner and expert rustaceans. For beginners it will hopefully teach them new
tools and clarify distinctions that were confusing them, and for experts it
will hopefully bring them up to date on changes in the error handling ecosystem
since they first started working with rust.</p>
<p>If theres time the section on libraries will also try to include a bit of a
brief history on the evolution of error handling libraries and which libraries
have been superceded.</p>Rustconf 2020 CFP ProposalBuilding Your Own Error Type: Part 12020-10-18T10:30:00+00:002020-10-18T10:30:00+00:00https://yaah.dev/building-your-own-error-type<p>I know this blog post looks long but I swear its mostly snippets of <code class="language-plaintext highlighter-rouge">rustc</code> errors that you can just skim past.</p>
<h1 id="introduction">Introduction</h1>
<p>Yesterday I had a friend on twitter <a href="https://twitter.com/zkat__/status/1317613730830512128?s=20">ask for help</a> creating an error type for a library that acted like anyhow but was suitable to expose as part of a library API. I tossed something together based on my experience writing error handling libraries. Then <a href="https://twitter.com/felipesere/status/1317790349880938497">another mutual</a> pointed out that they were amazed by the code snippet I’d posted and asked if I could write a blog post about it. So here I am.</p>
<p>I’ve decided to structure this explanation by going over the problem I was trying to solve, and how I built up the solution. The techniques here aren’t new or anything, the overall design is very similar to <code class="language-plaintext highlighter-rouge">std::io::Error</code> and the error kind pattern described in <a href="https://rust-lang-nursery.github.io/failure/">The Failure Book</a>, with a little hint of trait fun that I learned from working with the source code in <code class="language-plaintext highlighter-rouge">anyhow</code> in the process of writing <code class="language-plaintext highlighter-rouge">eyre</code>. I just applied the design patterns that are common in the ecosystem to this particular problem in the best way I could think of. Most importantly, I don’t want anyone coming away from this post thinking this is the best way to write an error type for a library, and that you should all copy this pattern. My goal is to get everyone comfortable mixing and matching design patterns to get an error type that matches their needs and best fits in to the rest of their software architecture.</p>
<h2 id="the-problem-and-plan">The Problem and Plan</h2>
<p>First let me summarize the needs that Kat described for their theoretical error type. It needed a programmatic interface suitable for a library. I interpreted this to mean that it needed an enum that could easily be matched upon to handle specific kinds of errors. It needed the ability to capture backtraces, and it needed the ability to add contextual stack traces to the error, which I interpreted to mean they wanted to add new error messages to the error without necessarily changing the error’s kind that you would match against, something like the <code class="language-plaintext highlighter-rouge">.wrap_err</code> method on <code class="language-plaintext highlighter-rouge">eyre</code> or the <code class="language-plaintext highlighter-rouge">.context</code> method on <code class="language-plaintext highlighter-rouge">anyhow</code>.</p>
<p>I formed a vague plan, I knew for the API I’d want to make an Error type and a Kind type, where the error was a struct with private internal members, and the Kind was a <code class="language-plaintext highlighter-rouge">non_exhaustive</code> enum. Getting the backtrace in there is pretty easy, just add a member for it to the outer <code class="language-plaintext highlighter-rouge">Error</code> type and capture it whenever an <code class="language-plaintext highlighter-rouge">Error</code> is constructed. The last feature is where I had to get a little creative, my plan was to make a separate <code class="language-plaintext highlighter-rouge">ErrorMessage</code> type that could be constructed from arbitrary <code class="language-plaintext highlighter-rouge">impl Display</code> types, and which optionally stored another <code class="language-plaintext highlighter-rouge">ErrorMessage</code> to act as the source to grab the previous error message whenever you add a new error message. I imagined an API like this:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">parse_config</span><span class="p">()</span> <span class="k">-></span> <span class="n">Result</span><span class="o"><</span><span class="p">(),</span> <span class="n">Error</span><span class="o">></span> <span class="p">{</span>
<span class="nf">Err</span><span class="p">(</span><span class="nn">Kind</span><span class="p">::</span><span class="n">NoFile</span><span class="p">)</span><span class="nf">.wrap_err</span><span class="p">(</span><span class="s">"config is invalid"</span><span class="p">)</span><span class="o">?</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Where when you print this error with an error reporter like <code class="language-plaintext highlighter-rouge">anyhow</code> or <code class="language-plaintext highlighter-rouge">eyre</code> you’d get:</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Error:
0: config is invalid
1: file not found
Backtrace:
...
</code></pre></div></div>
<p>Or something along those lines.</p>
<h3 id="programmatic-api---aka-reacting-to-specific-errors">Programmatic API - aka Reacting To Specific Errors</h3>
<p>I started by sketching out the API and the basic types. First with just the handling bit because it seemed easiest. I split it into a <code class="language-plaintext highlighter-rouge">lib.rs</code> file and another <code class="language-plaintext highlighter-rouge">examples/report.rs</code> file to easily run the “test” to visually inspect the output and make sure that all the APIs worked together as expected:</p>
<p><code class="language-plaintext highlighter-rouge">lib.rs</code>:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#![allow(clippy::try_err)]</span>
<span class="k">pub</span> <span class="k">fn</span> <span class="nf">foo_handle</span><span class="p">()</span> <span class="k">-></span> <span class="n">Result</span><span class="o"><</span><span class="p">(),</span> <span class="n">Error</span><span class="o">></span> <span class="p">{</span>
<span class="nf">Err</span><span class="p">(</span><span class="nn">Kind</span><span class="p">::</span><span class="n">Important</span><span class="p">)</span><span class="o">?</span>
<span class="p">}</span>
<span class="k">pub</span> <span class="k">fn</span> <span class="nf">foo_no_handle</span><span class="p">()</span> <span class="k">-></span> <span class="n">Result</span><span class="o"><</span><span class="p">(),</span> <span class="n">Error</span><span class="o">></span> <span class="p">{</span>
<span class="nf">Err</span><span class="p">(</span><span class="nn">Kind</span><span class="p">::</span><span class="n">NotImportant</span><span class="p">)</span><span class="o">?</span>
<span class="p">}</span>
<span class="k">pub</span> <span class="k">struct</span> <span class="n">Error</span> <span class="p">{</span>
<span class="n">kind</span><span class="p">:</span> <span class="n">Kind</span><span class="p">,</span>
<span class="p">}</span>
<span class="nd">#[non_exhaustive]</span>
<span class="k">pub</span> <span class="k">enum</span> <span class="n">Kind</span> <span class="p">{</span>
<span class="n">Important</span><span class="p">,</span>
<span class="n">NotImportant</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">examples/report.rs</code>:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="k">-></span> <span class="n">Result</span><span class="o"><</span><span class="p">(),</span> <span class="nn">eyre</span><span class="p">::</span><span class="n">Report</span><span class="o">></span> <span class="p">{</span>
<span class="k">match</span> <span class="nn">playground</span><span class="p">::</span><span class="nf">foo_no_handle</span><span class="p">()</span> <span class="p">{</span>
<span class="nf">Ok</span><span class="p">(</span><span class="mi">_</span><span class="p">)</span> <span class="k">=></span> <span class="p">{}</span>
<span class="nf">Err</span><span class="p">(</span><span class="n">e</span><span class="p">)</span> <span class="k">if</span> <span class="n">e</span><span class="nf">.kind</span><span class="p">()</span> <span class="o">==</span> <span class="nn">playground</span><span class="p">::</span><span class="nn">Kind</span><span class="p">::</span><span class="n">Important</span> <span class="k">=></span> <span class="nf">Err</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="o">?</span><span class="p">,</span>
<span class="nf">Err</span><span class="p">(</span><span class="mi">_</span><span class="p">)</span> <span class="k">=></span> <span class="p">{}</span>
<span class="p">}</span>
<span class="k">match</span> <span class="nn">playground</span><span class="p">::</span><span class="nf">foo_handle</span><span class="p">()</span> <span class="p">{</span>
<span class="nf">Ok</span><span class="p">(</span><span class="mi">_</span><span class="p">)</span> <span class="k">=></span> <span class="p">{}</span>
<span class="nf">Err</span><span class="p">(</span><span class="n">e</span><span class="p">)</span> <span class="k">if</span> <span class="n">e</span><span class="nf">.kind</span><span class="p">()</span> <span class="o">==</span> <span class="nn">playground</span><span class="p">::</span><span class="nn">Kind</span><span class="p">::</span><span class="n">Important</span> <span class="k">=></span> <span class="nf">Err</span><span class="p">(</span><span class="n">e</span><span class="p">)</span><span class="o">?</span><span class="p">,</span>
<span class="nf">Err</span><span class="p">(</span><span class="mi">_</span><span class="p">)</span> <span class="k">=></span> <span class="p">{}</span>
<span class="p">}</span>
<span class="nf">Ok</span><span class="p">(())</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Now that I’ve got that setup here’s where I like to let <code class="language-plaintext highlighter-rouge">rustc</code> take the driver’s seat so lets see what it says.</p>
<pre><font color="#F15D22"><b>error[E0277]</b></font><b>: `?` couldn't convert the error to `Error`</b>
<font color="#48B9C7"><b>--> </b></font>src/lib.rs:8:28
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>7</b></font> <font color="#48B9C7"><b>| </b></font>pub fn foo_no_handle() -> Result<(), Error> {
<font color="#48B9C7"><b>| </b></font> <font color="#48B9C7"><b>-----------------</b></font> <font color="#48B9C7"><b>expected `Error` because of this</b></font>
<font color="#48B9C7"><b>8</b></font> <font color="#48B9C7"><b>| </b></font> Err(Kind::NotImportant)?
<font color="#48B9C7"><b>| </b></font> <font color="#F15D22"><b>^</b></font> <font color="#F15D22"><b>the trait `From<Kind>` is not implemented for `Error`</b></font>
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>= </b></font><b>note</b>: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
<font color="#48B9C7"><b>= </b></font><b>note</b>: required by `from`
<font color="#F15D22"><b>error</b></font><b>: aborting due to 2 previous errors</b></pre>
<p>Alright, lets go ahead and add that rq:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span> <span class="n">From</span><span class="o"><</span><span class="n">Kind</span><span class="o">></span> <span class="k">for</span> <span class="n">Error</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">from</span><span class="p">(</span><span class="n">kind</span><span class="p">:</span> <span class="n">Kind</span><span class="p">)</span> <span class="k">-></span> <span class="n">Self</span> <span class="p">{</span>
<span class="n">Self</span> <span class="p">{</span> <span class="n">kind</span> <span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">rustc</code>:</p>
<pre><font color="#4E9A06"><b>❯</b></font> cargo check --example report
<font color="#C4A000"><b>warning</b></font><b>: field is never read: `kind`</b>
<font color="#48B9C7"><b>--> </b></font>src/lib.rs:12:5
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>12</b></font> <font color="#48B9C7"><b>| </b></font> kind: Kind,
<font color="#48B9C7"><b>| </b></font> <font color="#C4A000"><b>^^^^^^^^^^</b></font>
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>= </b></font><b>note</b>: `#[warn(dead_code)]` on by default
<font color="#C4A000"><b>warning</b></font><b>: 1 warning emitted</b>
<font color="#4E9A06"><b> Checking</b></font> playground v0.1.0 (/home/jlusby/playground)
<font color="#F15D22"><b>error[E0599]</b></font><b>: no method named `kind` found for struct `playground::Error` in the current scope</b>
<font color="#48B9C7"><b>--> </b></font>examples/report.rs:4:21
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>4</b></font> <font color="#48B9C7"><b>| </b></font> Err(e) if e.kind() == playground::Kind::Important => Err(e)?,
<font color="#48B9C7"><b>| </b></font> <font color="#F15D22"><b>^^^^</b></font> <font color="#F15D22"><b>private field, not a method</b></font></pre>
<p>Alright, lets go silence that dead code warning rq and then add the kind method next:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#![allow(dead_code)]</span>
<span class="k">impl</span> <span class="n">Error</span> <span class="p">{</span>
<span class="k">pub</span> <span class="k">fn</span> <span class="nf">kind</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">)</span> <span class="k">-></span> <span class="n">Kind</span> <span class="p">{</span>
<span class="k">self</span><span class="py">.kind</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">rustc</code>:</p>
<pre><font color="#4E9A06"><b>❯</b></font> cargo check --example report
<font color="#4E9A06"><b> Checking</b></font> playground v0.1.0 (/home/jlusby/playground)
<font color="#F15D22"><b>error[E0507]</b></font><b>: cannot move out of `self.kind` which is behind a shared reference</b>
<font color="#48B9C7"><b>--> </b></font>src/lib.rs:18:9
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>18</b></font> <font color="#48B9C7"><b>| </b></font> self.kind
<font color="#48B9C7"><b>| </b></font> <font color="#F15D22"><b>^^^^^^^^^</b></font> <font color="#F15D22"><b>move occurs because `self.kind` has type `Kind`, which does not implement the `Copy` trait</b></font>
<font color="#F15D22"><b>error</b></font><b>: aborting due to previous error</b>
</pre>
<p>Oops, forgot to impl copy, lets go add that rq, and <code class="language-plaintext highlighter-rouge">Debug</code> while we’re at it:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[derive(Debug,</span> <span class="nd">Copy,</span> <span class="nd">Clone)]</span>
<span class="nd">#[non_exhaustive]</span>
<span class="k">pub</span> <span class="k">enum</span> <span class="n">Kind</span> <span class="p">{</span>
<span class="n">Important</span><span class="p">,</span>
<span class="n">NotImportant</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">rustc</code>:</p>
<pre><font color="#4E9A06"><b>❯</b></font> cargo check --example report
<font color="#4E9A06"><b> Checking</b></font> playground v0.1.0 (/home/jlusby/playground)
<font color="#F15D22"><b>error[E0369]</b></font><b>: binary operation `==` cannot be applied to type `Kind`</b>
<font color="#48B9C7"><b>--> </b></font>examples/report.rs:4:28
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>4</b></font> <font color="#48B9C7"><b>| </b></font> Err(e) if e.kind() == playground::Kind::Important => Err(e)?,
<font color="#48B9C7"><b>| </b></font> <font color="#48B9C7"><b>--------</b></font> <font color="#F15D22"><b>^^</b></font> <font color="#48B9C7"><b>---------------------------</b></font> <font color="#48B9C7"><b>Kind</b></font>
<font color="#48B9C7"><b>| </b></font> <font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>| </b></font> <font color="#48B9C7"><b>Kind</b></font>
<font color="#F15D22"><b>error[E0277]</b></font><b>: the trait bound `playground::Error: std::error::Error` is not satisfied</b>
<font color="#48B9C7"><b>--> </b></font>examples/report.rs:4:68
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>4</b></font> <font color="#48B9C7"><b>| </b></font> Err(e) if e.kind() == playground::Kind::Important => Err(e)?,
<font color="#48B9C7"><b>| </b></font> <font color="#F15D22"><b>^</b></font> <font color="#F15D22"><b>the trait `std::error::Error` is not implemented for `playground::Error`</b></font>
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>= </b></font><b>note</b>: required because of the requirements on the impl of `From<playground::Error>` for `Report`
<font color="#48B9C7"><b>= </b></font><b>note</b>: required by `from`
</pre>
<p>Looks like it’s time to add some more traits to <code class="language-plaintext highlighter-rouge">Kind</code> and <code class="language-plaintext highlighter-rouge">Error</code>, if memory serves you only need <code class="language-plaintext highlighter-rouge">PartialEq</code> to use <code class="language-plaintext highlighter-rouge">==</code>, though I’m surprised the error message here doesn’t indicate that:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// struct Error { ... }</span>
<span class="k">impl</span> <span class="nn">std</span><span class="p">::</span><span class="nn">error</span><span class="p">::</span><span class="n">Error</span> <span class="k">for</span> <span class="n">Error</span> <span class="p">{}</span>
<span class="nd">#[derive(Debug,</span> <span class="nd">Copy,</span> <span class="nd">Clone,</span> <span class="nd">PartialEq)]</span>
<span class="nd">#[non_exhaustive]</span>
<span class="k">pub</span> <span class="k">enum</span> <span class="n">Kind</span> <span class="p">{</span>
<span class="n">Important</span><span class="p">,</span>
<span class="n">NotImportant</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>
<p>At this point I know rustc is gonna tell me to add <code class="language-plaintext highlighter-rouge">Debug</code> and <code class="language-plaintext highlighter-rouge">Display</code> impls to <code class="language-plaintext highlighter-rouge">Error</code> so I’ll just go ahead and do that myself. In this case however I know that I’m going to change this later, but for now to keep the example easier for a blog post I’m just gonna use the <code class="language-plaintext highlighter-rouge">Display</code> impl on <code class="language-plaintext highlighter-rouge">Kind</code> as the source of the <code class="language-plaintext highlighter-rouge">Display</code> impl on <code class="language-plaintext highlighter-rouge">Error</code>, which I can later change around when I add my <code class="language-plaintext highlighter-rouge">.wrap_err</code> function.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="n">fmt</span><span class="p">;</span>
<span class="k">impl</span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Display</span> <span class="k">for</span> <span class="n">Error</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">fmt</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">,</span> <span class="n">f</span><span class="p">:</span> <span class="o">&</span><span class="k">mut</span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Formatter</span><span class="o"><</span><span class="nv">'_</span><span class="o">></span><span class="p">)</span> <span class="k">-></span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Result</span> <span class="p">{</span>
<span class="k">self</span><span class="py">.kind</span><span class="nf">.fmt</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">impl</span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Display</span> <span class="k">for</span> <span class="n">Kind</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">fmt</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">,</span> <span class="n">f</span><span class="p">:</span> <span class="o">&</span><span class="k">mut</span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Formatter</span><span class="o"><</span><span class="nv">'_</span><span class="o">></span><span class="p">)</span> <span class="k">-></span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Result</span> <span class="p">{</span>
<span class="k">use</span> <span class="nn">Kind</span><span class="p">::</span><span class="o">*</span><span class="p">;</span>
<span class="k">match</span> <span class="k">self</span> <span class="p">{</span>
<span class="n">Important</span> <span class="k">=></span> <span class="nd">write!</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"this error was important to us"</span><span class="p">),</span>
<span class="n">NotImportant</span> <span class="k">=></span> <span class="nd">write!</span><span class="p">(</span><span class="n">f</span><span class="p">,</span> <span class="s">"this error was not a place of honor"</span><span class="p">),</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Now <code class="language-plaintext highlighter-rouge">rustc</code> should be happy, lets see what it says.</p>
<p><code class="language-plaintext highlighter-rouge">rustc</code>:</p>
<pre><font color="#4E9A06"><b>❯</b></font> cargo check --example report
<font color="#C4A000"><b>warning</b></font><b>: returning an `Err(_)` with the `?` operator</b>
<font color="#48B9C7"><b>--> </b></font>examples/report.rs:4:62
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>4</b></font> <font color="#48B9C7"><b>| </b></font> Err(e) if e.kind() == playground::Kind::Important => Err(e)?,
<font color="#48B9C7"><b>| </b></font> <font color="#C4A000"><b>^^^^^^^</b></font> <font color="#C4A000"><b>help: try this: `return Err(e.into())`</b></font>
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>= </b></font><b>note</b>: `#[warn(clippy::try_err)]` on by default
<font color="#48B9C7"><b>= </b></font><b>help</b>: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#try_err
<font color="#C4A000"><b>warning</b></font><b>: returning an `Err(_)` with the `?` operator</b>
<font color="#48B9C7"><b>--> </b></font>examples/report.rs:10:62
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>10</b></font> <font color="#48B9C7"><b>| </b></font> Err(e) if e.kind() == playground::Kind::Important => Err(e)?,
<font color="#48B9C7"><b>| </b></font> <font color="#C4A000"><b>^^^^^^^</b></font> <font color="#C4A000"><b>help: try this: `return Err(e.into())`</b></font>
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>= </b></font><b>help</b>: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#try_err
<font color="#C4A000"><b>warning</b></font><b>: 2 warnings emitted</b>
<font color="#4E9A06"><b> Finished</b></font> dev [unoptimized + debuginfo] target(s) in 0.00s
</pre>
<p>Me: <em>I really need to disable this warning in clippy, ugh</em></p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#![allow(clippy::try_err)]</span>
</code></pre></div></div>
<p>Okay, it should work now, lets see our initial report format:</p>
<p><code class="language-plaintext highlighter-rouge">cargo</code>:</p>
<pre><font color="#4E9A06"><b>❯</b></font> cargo run --example report
<font color="#4E9A06"><b> Compiling</b></font> playground v0.1.0 (/home/jlusby/playground)
<font color="#4E9A06"><b> Finished</b></font> dev [unoptimized + debuginfo] target(s) in 0.23s
<font color="#4E9A06"><b> Running</b></font> `target/debug/examples/report`
Error: this error was important to us
Location:
examples/report.rs:10:68
</pre>
<p>Beautiful, alright so now we’ve got our initial error type setup. We compile, we show nice error messages, we create it conveniently from a <code class="language-plaintext highlighter-rouge">Kind</code> and the <code class="language-plaintext highlighter-rouge">?</code> operator, and we have an outer <code class="language-plaintext highlighter-rouge">struct</code> type to work with to start adding our new features. Lets move onto backtrace next.</p>
<h2 id="backtrace">Backtrace</h2>
<p>Here I’ll start by just slapping a <code class="language-plaintext highlighter-rouge">Backtrace</code> in the struct and let rustc remind me what features to enable. In this example I’m going to use <code class="language-plaintext highlighter-rouge">std::backtrace::Backtrace</code> because it’s easier to work with and is compatible with <code class="language-plaintext highlighter-rouge">eyre</code> and <code class="language-plaintext highlighter-rouge">anyhow</code> by default. I could make this work with <code class="language-plaintext highlighter-rouge">backtrace::Backtrace</code> from the backtrace-rs crate but its a bit more involved so I’ll leave that to a later post if people are interested. Hopefully it won’t be needed however because backtrace stabilization is starting to move forward again ^_^</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">std</span><span class="p">::</span><span class="nn">backtrace</span><span class="p">::</span><span class="n">Backtrace</span><span class="p">;</span>
<span class="nd">#[derive(Debug)]</span>
<span class="k">pub</span> <span class="k">struct</span> <span class="n">Error</span> <span class="p">{</span>
<span class="n">kind</span><span class="p">:</span> <span class="n">Kind</span><span class="p">,</span>
<span class="n">backtrace</span><span class="p">:</span> <span class="n">Backtrace</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Alright <code class="language-plaintext highlighter-rouge">rustc</code>, gimmi your worst:</p>
<p><code class="language-plaintext highlighter-rouge">rustc</code>:</p>
<pre><font color="#4E9A06"><b>❯</b></font> cargo check --example report
<font color="#4E9A06"><b> Checking</b></font> playground v0.1.0 (/home/jlusby/playground)
<font color="#F15D22"><b>error[E0658]</b></font><b>: use of unstable library feature 'backtrace'</b>
<font color="#48B9C7"><b>--> </b></font>src/lib.rs:4:5
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>4</b></font> <font color="#48B9C7"><b>| </b></font>use std::backtrace::Backtrace;
<font color="#48B9C7"><b>| </b></font> <font color="#F15D22"><b>^^^^^^^^^^^^^^^^^^^^^^^^^</b></font>
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>= </b></font><b>note</b>: see issue #53487 <https://github.com/rust-lang/rust/issues/53487> for more information
<font color="#48B9C7"><b>= </b></font><b>help</b>: add `#![feature(backtrace)]` to the crate attributes to enable
<font color="#F15D22"><b>error[E0658]</b></font><b>: use of unstable library feature 'backtrace'</b>
<font color="#48B9C7"><b>--> </b></font>src/lib.rs:18:16
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>18</b></font> <font color="#48B9C7"><b>| </b></font> backtrace: Backtrace,
<font color="#48B9C7"><b>| </b></font> <font color="#F15D22"><b>^^^^^^^^^</b></font>
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>= </b></font><b>note</b>: see issue #53487 <https://github.com/rust-lang/rust/issues/53487> for more information
<font color="#48B9C7"><b>= </b></font><b>help</b>: add `#![feature(backtrace)]` to the crate attributes to enable
<font color="#F15D22"><b>error[E0063]</b></font><b>: missing field `backtrace` in initializer of `Error`</b>
<font color="#48B9C7"><b>--> </b></font>src/lib.rs:37:9
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>37</b></font> <font color="#48B9C7"><b>| </b></font> Self { kind }
<font color="#48B9C7"><b>| </b></font> <font color="#F15D22"><b>^^^^</b></font> <font color="#F15D22"><b>missing `backtrace`</b></font>
<font color="#F15D22"><b>error</b></font><b>: aborting due to 3 previous errors</b>
<b>Some errors have detailed explanations: E0063, E0658.</b>
<b>For more information about an error, try `rustc --explain E0063`.</b>
<font color="#CC0000"><b>error</b></font><b>:</b> could not compile `playground`
</pre>
<p>Alright so lets enable that feature, I already know I gotta switch to nightly so I’ll start using that too:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#![feature(backtrace)]</span>
<span class="k">impl</span> <span class="n">From</span><span class="o"><</span><span class="n">Kind</span><span class="o">></span> <span class="k">for</span> <span class="n">Error</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">from</span><span class="p">(</span><span class="n">kind</span><span class="p">:</span> <span class="n">Kind</span><span class="p">)</span> <span class="k">-></span> <span class="n">Self</span> <span class="p">{</span>
<span class="n">Self</span> <span class="p">{</span>
<span class="n">kind</span><span class="p">,</span>
<span class="c">// and we gotta actually capture it when we construct an `Error`</span>
<span class="n">backtrace</span><span class="p">:</span> <span class="nn">Backtrace</span><span class="p">::</span><span class="nf">capture</span><span class="p">(),</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>At this point rustc was immediately happy, so let’s go straight into running it:</p>
<p><code class="language-plaintext highlighter-rouge">cargo</code>:</p>
<pre><font color="#4E9A06"><b>❯</b></font> RUST_LIB_BACKTRACE=1 cargo +nightly run --example report
<font color="#4E9A06"><b> Finished</b></font> dev [unoptimized + debuginfo] target(s) in 0.01s
<font color="#4E9A06"><b> Running</b></font> `target/debug/examples/report`
Error: this error was important to us
Location:
examples/report.rs:12:68
Stack backtrace:
0: eyre::DefaultHandler::default_with
at /home/jlusby/.cargo/registry/src/github.com-1ecc6299db9ec823/eyre-0.6.1/src/lib.rs:689
1: core::ops::function::Fn::call
at /home/jlusby/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:70
2: eyre::capture_handler
at /home/jlusby/.cargo/registry/src/github.com-1ecc6299db9ec823/eyre-0.6.1/src/lib.rs:551
3: eyre::error::<impl eyre::Report>::from_std
at /home/jlusby/.cargo/registry/src/github.com-1ecc6299db9ec823/eyre-0.6.1/src/error.rs:87
4: eyre::error::<impl core::convert::From<E> for eyre::Report>::from
at /home/jlusby/.cargo/registry/src/github.com-1ecc6299db9ec823/eyre-0.6.1/src/error.rs:461
5: report::main
at ./examples/report.rs:12
6: core::ops::function::FnOnce::call_once
at /home/jlusby/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:227
7: std::sys_common::backtrace::__rust_begin_short_backtrace
at /home/jlusby/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys_common/backtrace.rs:137
8: std::rt::lang_start::
at /home/jlusby/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:66
9: core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once
at /rustc/3525087ada7018ef227b10846648660b7f07b6d1/library/core/src/ops/function.rs:259
std::panicking::try::do_call
at /rustc/3525087ada7018ef227b10846648660b7f07b6d1/library/std/src/panicking.rs:381
std::panicking::try
at /rustc/3525087ada7018ef227b10846648660b7f07b6d1/library/std/src/panicking.rs:345
std::panic::catch_unwind
at /rustc/3525087ada7018ef227b10846648660b7f07b6d1/library/std/src/panic.rs:382
std::rt::lang_start_internal
at /rustc/3525087ada7018ef227b10846648660b7f07b6d1/library/std/src/rt.rs:51
10: std::rt::lang_start
at /home/jlusby/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:65
11: main
12: __libc_start_main
13: _start
</pre>
<p><em>looks at backtrace</em> hmm, something’s not right here, oh shit, I forgot to return the backtrace type via the <code class="language-plaintext highlighter-rouge">Error</code> trait, okay lets go back and add that.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span> <span class="nn">std</span><span class="p">::</span><span class="nn">error</span><span class="p">::</span><span class="n">Error</span> <span class="k">for</span> <span class="n">Error</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">backtrace</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">)</span> <span class="k">-></span> <span class="nb">Option</span><span class="o"><&</span><span class="n">Backtrace</span><span class="o">></span> <span class="p">{</span>
<span class="nf">Some</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="py">.backtrace</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">cargo</code>:</p>
<pre><font color="#4E9A06"><b>❯</b></font> RUST_LIB_BACKTRACE=1 cargo +nightly run --example report
<font color="#4E9A06"><b> Finished</b></font> dev [unoptimized + debuginfo] target(s) in 0.00s
<font color="#4E9A06"><b> Running</b></font> `target/debug/examples/report`
Error: this error was important to us
Location:
examples/report.rs:12:68
Stack backtrace:
0: <playground::Error as core::convert::From<playground::Kind>>::from
at ./src/lib.rs:44
1: playground::foo_handle
at ./src/lib.rs:9
2: report::main
at ./examples/report.rs:10
3: core::ops::function::FnOnce::call_once
at /home/jlusby/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:227
4: std::sys_common::backtrace::__rust_begin_short_backtrace
at /home/jlusby/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys_common/backtrace.rs:137
5: std::rt::lang_start::
at /home/jlusby/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:66
6: core::ops::function::impls::<impl core::ops::function::FnOnce<A> for &F>::call_once
at /rustc/3525087ada7018ef227b10846648660b7f07b6d1/library/core/src/ops/function.rs:259
std::panicking::try::do_call
at /rustc/3525087ada7018ef227b10846648660b7f07b6d1/library/std/src/panicking.rs:381
std::panicking::try
at /rustc/3525087ada7018ef227b10846648660b7f07b6d1/library/std/src/panicking.rs:345
std::panic::catch_unwind
at /rustc/3525087ada7018ef227b10846648660b7f07b6d1/library/std/src/panic.rs:382
std::rt::lang_start_internal
at /rustc/3525087ada7018ef227b10846648660b7f07b6d1/library/std/src/rt.rs:51
7: std::rt::lang_start
at /home/jlusby/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:65
8: main
9: __libc_start_main
10: _start
</pre>
<p>There we go, perfect. We can see the error being constructed from the <code class="language-plaintext highlighter-rouge">Kind</code> in the <code class="language-plaintext highlighter-rouge">playground</code> library I’m using to write this example, looks like we’re done here. Now comes the fun part, adding new error messages to the chain of errors.</p>
<h2 id="context-stack-traces-aka-wrapping-errors-internally">Context Stack Traces aka Wrapping Errors Internally</h2>
<p>Here I’m gonna start much like I did before, I’ll sketch out the new API and fill it in based on compiler errors. I tweek the example throwing functions to add the extra method call I want to make. In this case however I know that the new method I’m adding will not be added as an inherent method on <code class="language-plaintext highlighter-rouge">Kind</code> or <code class="language-plaintext highlighter-rouge">Error</code>, in order to use it through <code class="language-plaintext highlighter-rouge">Result</code> the same way you can with <code class="language-plaintext highlighter-rouge">eyre</code> and <code class="language-plaintext highlighter-rouge">anyhow</code> you need to do it as an extension trait. I’ll go ahead and define that trait as well.</p>
<p>Disclaimer, the trait I’m going to define here is something that I first learned about from <code class="language-plaintext highlighter-rouge">anyhow</code>’s source and it took me months to get good enough with traits to figure out how to write it as cleanly as this. Even then when I was first writing this example I had to go look up <a href="https://docs.rs/color-eyre/0.5.6/src/color_eyre/section/mod.rs.html#135-318">the source code</a> for the <code class="language-plaintext highlighter-rouge">Section</code> trait in <code class="language-plaintext highlighter-rouge">color-eyre</code> to remember exactly how it’s supposed to be done.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">fn</span> <span class="nf">foo_handle</span><span class="p">()</span> <span class="k">-></span> <span class="n">Result</span><span class="o"><</span><span class="p">(),</span> <span class="n">Error</span><span class="o">></span> <span class="p">{</span>
<span class="nf">Err</span><span class="p">(</span><span class="nn">Kind</span><span class="p">::</span><span class="n">Important</span><span class="p">)</span><span class="nf">.wrap_err</span><span class="p">(</span><span class="s">"this error should not be ignored"</span><span class="p">)</span><span class="o">?</span>
<span class="p">}</span>
<span class="k">pub</span> <span class="k">fn</span> <span class="nf">foo_no_handle</span><span class="p">()</span> <span class="k">-></span> <span class="n">Result</span><span class="o"><</span><span class="p">(),</span> <span class="n">Error</span><span class="o">></span> <span class="p">{</span>
<span class="nf">Err</span><span class="p">(</span><span class="nn">Kind</span><span class="p">::</span><span class="n">NotImportant</span><span class="p">)</span><span class="nf">.wrap_err</span><span class="p">(</span><span class="s">"it's okay to ignore this error"</span><span class="p">)</span><span class="o">?</span>
<span class="p">}</span>
<span class="k">trait</span> <span class="n">WrapErr</span> <span class="p">{</span>
<span class="k">type</span> <span class="n">Return</span><span class="p">;</span>
<span class="k">fn</span> <span class="n">wrap_err</span><span class="o"><</span><span class="n">D</span><span class="o">></span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">msg</span><span class="p">:</span> <span class="n">D</span><span class="p">)</span> <span class="k">-></span> <span class="nn">Self</span><span class="p">::</span><span class="n">Return</span>
<span class="k">where</span>
<span class="n">D</span><span class="p">:</span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Display</span> <span class="o">+</span> <span class="nb">Send</span> <span class="o">+</span> <span class="n">Sync</span> <span class="o">+</span> <span class="nv">'static</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<pre><font color="#4E9A06"><b>❯</b></font> cargo +nightly run --example report
<font color="#4E9A06"><b> Compiling</b></font> playground v0.1.0 (/home/jlusby/playground)
<font color="#F15D22"><b>error[E0599]</b></font><b>: no method named `wrap_err` found for enum `std::result::Result<_, Kind>` in the current scope</b>
<font color="#48B9C7"><b>--> </b></font>src/lib.rs:9:26
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>9</b></font> <font color="#48B9C7"><b>| </b></font> Err(Kind::Important).wrap_err("this error should not be ignored")?
<font color="#48B9C7"><b>| </b></font> <font color="#F15D22"><b>^^^^^^^^</b></font> <font color="#F15D22"><b>help: there is an associated function with a similar name: `map_err`</b></font>
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>= </b></font><b>help</b>: items from traits can only be used if the trait is implemented and in scope
<font color="#73C48F"><b>note</b></font>: `WrapErr` defines an item `wrap_err`, perhaps you need to implement it
<font color="#48B9C7"><b>--> </b></font>src/lib.rs:67:1
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>67</b></font> <font color="#48B9C7"><b>| </b></font>trait WrapErr {
<font color="#48B9C7"><b>| </b></font><font color="#73C48F"><b>^^^^^^^^^^^^^</b></font>
</pre>
<p>So good so far. The cool thing that this trait in <code class="language-plaintext highlighter-rouge">eyre</code> and friends does is convert the inner error type too an <code class="language-plaintext highlighter-rouge">eyre::Report</code> immediately before adding the new error message to the report. However, going straight for this feature and implementing <code class="language-plaintext highlighter-rouge">WrapErr</code> for the generic set <code class="language-plaintext highlighter-rouge"><E: Error></code> will get you into trouble, because <code class="language-plaintext highlighter-rouge">rustc</code> won’t be able to be sure that <code class="language-plaintext highlighter-rouge">Result<...></code> can’t also be turned into your <code class="language-plaintext highlighter-rouge">Error</code> type.</p>
<p>The key here is to split it into two impls, the first impl, directly on your <code class="language-plaintext highlighter-rouge">Error</code> type, just handles adding the context, then your second impl and beyond all handle just the conversions and <code class="language-plaintext highlighter-rouge">map_err</code>ness or w/e. Here’s what it ends up looking like:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span> <span class="n">WrapErr</span> <span class="k">for</span> <span class="n">Error</span> <span class="p">{</span>
<span class="k">type</span> <span class="n">Return</span> <span class="o">=</span> <span class="n">Error</span><span class="p">;</span>
<span class="k">fn</span> <span class="n">wrap_err</span><span class="o"><</span><span class="n">D</span><span class="o">></span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">msg</span><span class="p">:</span> <span class="n">D</span><span class="p">)</span> <span class="k">-></span> <span class="nn">Self</span><span class="p">::</span><span class="n">Return</span>
<span class="k">where</span>
<span class="n">D</span><span class="p">:</span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Display</span> <span class="o">+</span> <span class="nb">Send</span> <span class="o">+</span> <span class="n">Sync</span> <span class="o">+</span> <span class="nv">'static</span><span class="p">,</span>
<span class="p">{</span>
<span class="nd">todo!</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">impl</span><span class="o"><</span><span class="n">T</span><span class="p">,</span> <span class="n">E</span><span class="o">></span> <span class="n">WrapErr</span> <span class="k">for</span> <span class="n">Result</span><span class="o"><</span><span class="n">T</span><span class="p">,</span> <span class="n">E</span><span class="o">></span>
<span class="k">where</span>
<span class="n">E</span><span class="p">:</span> <span class="n">Into</span><span class="o"><</span><span class="n">Error</span><span class="o">></span><span class="p">,</span>
<span class="p">{</span>
<span class="k">type</span> <span class="n">Return</span> <span class="o">=</span> <span class="n">Result</span><span class="o"><</span><span class="n">T</span><span class="p">,</span> <span class="n">Error</span><span class="o">></span><span class="p">;</span>
<span class="k">fn</span> <span class="n">wrap_err</span><span class="o"><</span><span class="n">D</span><span class="o">></span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">msg</span><span class="p">:</span> <span class="n">D</span><span class="p">)</span> <span class="k">-></span> <span class="nn">Self</span><span class="p">::</span><span class="n">Return</span>
<span class="k">where</span>
<span class="n">D</span><span class="p">:</span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Display</span> <span class="o">+</span> <span class="nb">Send</span> <span class="o">+</span> <span class="n">Sync</span> <span class="o">+</span> <span class="nv">'static</span><span class="p">,</span>
<span class="p">{</span>
<span class="nd">todo!</span><span class="p">()</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>For now I’m going to leave this unimplemented, because we haven’t yet created the spot to store our error messages, rustc seems pretty happy with us tho, the only complaint is about the unused <code class="language-plaintext highlighter-rouge">msg</code> argument.</p>
<p>The next step is we need a way to store an <code class="language-plaintext highlighter-rouge">ErrorMessage</code>, and a previous error message, such that we can create a linked list of sources internally without losing access to our <code class="language-plaintext highlighter-rouge">Kind</code> or <code class="language-plaintext highlighter-rouge">Backtrace</code>.</p>
<p>We start with a definition for the error message struct that looks like a singly linked list:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">ErrorMessage</span> <span class="p">{</span>
<span class="n">message</span><span class="p">:</span> <span class="nb">Box</span><span class="o"><</span><span class="n">dyn</span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Display</span> <span class="o">+</span> <span class="nb">Send</span> <span class="o">+</span> <span class="n">Sync</span> <span class="o">+</span> <span class="nv">'static</span><span class="o">></span><span class="p">,</span>
<span class="n">source</span><span class="p">:</span> <span class="nb">Option</span><span class="o"><</span><span class="nb">Box</span><span class="o"><</span><span class="n">ErrorMessage</span><span class="o">>></span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Add this to our <code class="language-plaintext highlighter-rouge">Error</code> type:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#[derive(Debug)]</span>
<span class="k">pub</span> <span class="k">struct</span> <span class="n">Error</span> <span class="p">{</span>
<span class="n">kind</span><span class="p">:</span> <span class="n">Kind</span><span class="p">,</span>
<span class="n">backtrace</span><span class="p">:</span> <span class="n">Backtrace</span><span class="p">,</span>
<span class="n">message</span><span class="p">:</span> <span class="n">ErrorMessage</span><span class="p">,</span>
<span class="p">}</span>
</code></pre></div></div>
<p>And adjust our <code class="language-plaintext highlighter-rouge">Display</code> impl on <code class="language-plaintext highlighter-rouge">Error</code> to use the new message member instead of kind:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Display</span> <span class="k">for</span> <span class="n">Error</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">fmt</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">,</span> <span class="n">f</span><span class="p">:</span> <span class="o">&</span><span class="k">mut</span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Formatter</span><span class="o"><</span><span class="nv">'_</span><span class="o">></span><span class="p">)</span> <span class="k">-></span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Result</span> <span class="p">{</span>
<span class="k">self</span><span class="py">.message</span><span class="nf">.fmt</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>I’m already sure this will anger <code class="language-plaintext highlighter-rouge">rustc</code> so lets see where they tell us to start next:</p>
<p><code class="language-plaintext highlighter-rouge">rustc</code>:</p>
<pre><font color="#4E9A06"><b>❯</b></font> cargo +nightly check --example report
<font color="#4E9A06"><b> Checking</b></font> playground v0.1.0 (/home/jlusby/playground)
<font color="#F15D22"><b>error[E0277]</b></font><b>: `ErrorMessage` doesn't implement `Debug`</b>
<font color="#48B9C7"><b>--> </b></font>src/lib.rs:20:5
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>20</b></font> <font color="#48B9C7"><b>| </b></font> message: ErrorMessage,
<font color="#48B9C7"><b>| </b></font> <font color="#F15D22"><b>^^^^^^^^^^^^^^^^^^^^^</b></font> <font color="#F15D22"><b>`ErrorMessage` cannot be formatted using `{:?}`</b></font>
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>= </b></font><b>help</b>: the trait `Debug` is not implemented for `ErrorMessage`
<font color="#48B9C7"><b>= </b></font><b>note</b>: add `#[derive(Debug)]` or manually implement `Debug`
<font color="#48B9C7"><b>= </b></font><b>note</b>: required because of the requirements on the impl of `Debug` for `&ErrorMessage`
<font color="#48B9C7"><b>= </b></font><b>note</b>: required for the cast to the object type `dyn Debug`
<font color="#48B9C7"><b>= </b></font><b>note</b>: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
<font color="#F15D22"><b>error[E0599]</b></font><b>: no method named `fmt` found for struct `ErrorMessage` in the current scope</b>
<font color="#48B9C7"><b>--> </b></font>src/lib.rs:25:22
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>25</b></font> <font color="#48B9C7"><b>| </b></font> self.message.fmt(f)
<font color="#48B9C7"><b>| </b></font> <font color="#F15D22"><b>^^^</b></font> <font color="#F15D22"><b>method not found in `ErrorMessage`</b></font>
<font color="#48B9C7"><b>...</b></font>
<font color="#48B9C7"><b>101</b></font> <font color="#48B9C7"><b>| </b></font>struct ErrorMessage {
<font color="#48B9C7"><b>| -------------------</b></font> <font color="#48B9C7"><b>method `fmt` not found for this</b></font>
<font color="#48B9C7"><b>| </b></font>
<font color="#48B9C7"><b>::: </b></font>/home/jlusby/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/fmt/mod.rs:930:8
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>930</b></font> <font color="#48B9C7"><b>| </b></font> fn fmt(&self, f: &mut Formatter<'_>) -> Result;
<font color="#48B9C7"><b>| </b></font> <font color="#48B9C7"><b>---</b></font>
<font color="#48B9C7"><b>| </b></font> <font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>| </b></font> <font color="#48B9C7"><b>the method is available for `Box<ErrorMessage>` here</b></font>
<font color="#48B9C7"><b>| </b></font> <font color="#48B9C7"><b>the method is available for `Arc<ErrorMessage>` here</b></font>
<font color="#48B9C7"><b>| </b></font> <font color="#48B9C7"><b>the method is available for `Rc<ErrorMessage>` here</b></font>
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>= </b></font><b>help</b>: items from traits can only be used if the trait is implemented and in scope
<font color="#48B9C7"><b>= </b></font><b>note</b>: the following traits define an item `fmt`, perhaps you need to implement one of them:
candidate #1: `Debug`
candidate #2: `std::fmt::Display`
candidate #3: `Octal`
candidate #4: `Binary`
candidate #5: `LowerHex`
candidate #6: `UpperHex`
candidate #7: `Pointer`
candidate #8: `LowerExp`
candidate #9: `UpperExp`
<font color="#F15D22"><b>error[E0063]</b></font><b>: missing field `message` in initializer of `Error`</b>
<font color="#48B9C7"><b>--> </b></font>src/lib.rs:43:9
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>43</b></font> <font color="#48B9C7"><b>| </b></font> Self {
<font color="#48B9C7"><b>| </b></font> <font color="#F15D22"><b>^^^^</b></font> <font color="#F15D22"><b>missing `message`</b></font>
<font color="#F15D22"><b>error</b></font><b>: aborting due to 3 previous errors</b>
<b>Some errors have detailed explanations: E0063, E0277, E0599.</b>
<b>For more information about an error, try `rustc --explain E0063`.</b>
<font color="#CC0000"><b>error</b></font><b>:</b> could not compile `playground`
</pre>
<p>Whew, big error message, lets go add those impls rq:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span> <span class="n">From</span><span class="o"><</span><span class="n">Kind</span><span class="o">></span> <span class="k">for</span> <span class="n">Error</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">from</span><span class="p">(</span><span class="n">kind</span><span class="p">:</span> <span class="n">Kind</span><span class="p">)</span> <span class="k">-></span> <span class="n">Self</span> <span class="p">{</span>
<span class="n">Self</span> <span class="p">{</span>
<span class="n">kind</span><span class="p">,</span>
<span class="n">backtrace</span><span class="p">:</span> <span class="nn">Backtrace</span><span class="p">::</span><span class="nf">capture</span><span class="p">(),</span>
<span class="n">message</span><span class="p">:</span> <span class="nn">ErrorMessage</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="n">kind</span><span class="p">),</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nd">#[derive(Debug)]</span>
<span class="k">struct</span> <span class="n">ErrorMessage</span> <span class="p">{</span>
<span class="n">message</span><span class="p">:</span> <span class="nb">Box</span><span class="o"><</span><span class="n">dyn</span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Display</span> <span class="o">+</span> <span class="nb">Send</span> <span class="o">+</span> <span class="n">Sync</span> <span class="o">+</span> <span class="nv">'static</span><span class="o">></span><span class="p">,</span>
<span class="n">source</span><span class="p">:</span> <span class="nb">Option</span><span class="o"><</span><span class="nb">Box</span><span class="o"><</span><span class="n">ErrorMessage</span><span class="o">>></span><span class="p">,</span>
<span class="p">}</span>
<span class="k">impl</span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Display</span> <span class="k">for</span> <span class="n">ErrorMessage</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">fmt</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">,</span> <span class="n">f</span><span class="p">:</span> <span class="o">&</span><span class="k">mut</span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Formatter</span><span class="o"><</span><span class="nv">'_</span><span class="o">></span><span class="p">)</span> <span class="k">-></span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Result</span> <span class="p">{</span>
<span class="k">self</span><span class="py">.message</span><span class="nf">.fmt</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Here you’ll notice im using the <code class="language-plaintext highlighter-rouge">kind</code> itself to create the initial error message, since it’s copy. This seemed like a reasonable default to me, but feel free to take a different approach if you prefer.</p>
<p><code class="language-plaintext highlighter-rouge">rustc</code>:</p>
<pre><font color="#4E9A06"><b>❯</b></font> cargo +nightly check --example report
<font color="#4E9A06"><b> Checking</b></font> playground v0.1.0 (/home/jlusby/playground)
<font color="#F15D22"><b>error[E0308]</b></font><b>: mismatched types</b>
<font color="#48B9C7"><b>--> </b></font>src/lib.rs:46:41
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>46</b></font> <font color="#48B9C7"><b>| </b></font> message: ErrorMessage::from(kind),
<font color="#48B9C7"><b>| </b></font> <font color="#F15D22"><b>^^^^</b></font> <font color="#F15D22"><b>expected struct `ErrorMessage`, found enum `Kind`</b></font>
<font color="#F15D22"><b>error[E0277]</b></font><b>: `(dyn std::fmt::Display + Send + Sync + 'static)` doesn't implement `Debug`</b>
<font color="#48B9C7"><b>--> </b></font>src/lib.rs:104:5
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>104</b></font> <font color="#48B9C7"><b>| </b></font> message: Box<dyn fmt::Display + Send + Sync + 'static>,
<font color="#48B9C7"><b>| </b></font> <font color="#F15D22"><b>^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^</b></font> <font color="#F15D22"><b>`(dyn std::fmt::Display + Send + Sync + 'static)` cannot be formatted using `{:?}` because it doesn't implement `Debug`</b></font>
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>= </b></font><b>help</b>: the trait `Debug` is not implemented for `(dyn std::fmt::Display + Send + Sync + 'static)`
<font color="#48B9C7"><b>= </b></font><b>note</b>: required because of the requirements on the impl of `Debug` for `Box<(dyn std::fmt::Display + Send + Sync + 'static)>`
<font color="#48B9C7"><b>= </b></font><b>note</b>: required because of the requirements on the impl of `Debug` for `&Box<(dyn std::fmt::Display + Send + Sync + 'static)>`
<font color="#48B9C7"><b>= </b></font><b>note</b>: required for the cast to the object type `dyn Debug`
<font color="#48B9C7"><b>= </b></font><b>note</b>: this error originates in a derive macro (in Nightly builds, run with -Z macro-backtrace for more info)
<font color="#F15D22"><b>error</b></font><b>: aborting due to 2 previous errors</b>
</pre>
<p>Oh god damnit, you can’t derive <code class="language-plaintext highlighter-rouge">Debug</code> with a <code class="language-plaintext highlighter-rouge">dyn Display</code> type, thats annoying, okay w/e:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">struct</span> <span class="n">ErrorMessage</span> <span class="p">{</span>
<span class="n">message</span><span class="p">:</span> <span class="nb">Box</span><span class="o"><</span><span class="n">dyn</span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Display</span> <span class="o">+</span> <span class="nb">Send</span> <span class="o">+</span> <span class="n">Sync</span> <span class="o">+</span> <span class="nv">'static</span><span class="o">></span><span class="p">,</span>
<span class="n">source</span><span class="p">:</span> <span class="nb">Option</span><span class="o"><</span><span class="nb">Box</span><span class="o"><</span><span class="n">ErrorMessage</span><span class="o">>></span><span class="p">,</span>
<span class="p">}</span>
<span class="k">impl</span><span class="o"><</span><span class="n">D</span><span class="o">></span> <span class="n">From</span><span class="o"><</span><span class="n">D</span><span class="o">></span> <span class="k">for</span> <span class="n">ErrorMessage</span>
<span class="k">where</span>
<span class="n">D</span><span class="p">:</span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Display</span> <span class="o">+</span> <span class="nb">Send</span> <span class="o">+</span> <span class="n">Sync</span> <span class="o">+</span> <span class="nv">'static</span><span class="p">,</span>
<span class="p">{</span>
<span class="k">fn</span> <span class="nf">from</span><span class="p">(</span><span class="n">message</span><span class="p">:</span> <span class="n">D</span><span class="p">)</span> <span class="k">-></span> <span class="n">Self</span> <span class="p">{</span>
<span class="n">ErrorMessage</span> <span class="p">{</span>
<span class="n">message</span><span class="p">:</span> <span class="nn">Box</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">message</span><span class="p">),</span>
<span class="n">source</span><span class="p">:</span> <span class="nb">None</span><span class="p">,</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">impl</span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Debug</span> <span class="k">for</span> <span class="n">ErrorMessage</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">fmt</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">,</span> <span class="n">f</span><span class="p">:</span> <span class="o">&</span><span class="k">mut</span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Formatter</span><span class="o"><</span><span class="nv">'_</span><span class="o">></span><span class="p">)</span> <span class="k">-></span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Result</span> <span class="p">{</span>
<span class="k">self</span><span class="py">.message</span><span class="nf">.fmt</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Here I decided to go with a from impl for all <code class="language-plaintext highlighter-rouge">Display</code> types so that we can easily make error messages from whatever, I have secret plans for this later:</p>
<p><code class="language-plaintext highlighter-rouge">rustc</code>:</p>
<pre><font color="#4E9A06"><b>❯</b></font> cargo +nightly check --example report
<font color="#4E9A06"><b> Checking</b></font> playground v0.1.0 (/home/jlusby/playground)
<font color="#F15D22"><b>error[E0119]</b></font><b>: conflicting implementations of trait `std::convert::From<ErrorMessage>` for type `ErrorMessage`:</b>
<font color="#48B9C7"><b>--> </b></font>src/lib.rs:107:1
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>107</b></font> <font color="#48B9C7"><b>| </b></font><font color="#F15D22"><b>/</b></font> impl<D> From<D> for ErrorMessage
<font color="#48B9C7"><b>108</b></font> <font color="#48B9C7"><b>| </b></font><font color="#F15D22"><b>|</b></font> where
<font color="#48B9C7"><b>109</b></font> <font color="#48B9C7"><b>| </b></font><font color="#F15D22"><b>|</b></font> D: fmt::Display + Send + Sync + 'static,
<font color="#48B9C7"><b>110</b></font> <font color="#48B9C7"><b>| </b></font><font color="#F15D22"><b>|</b></font> {
<font color="#48B9C7"><b>...</b></font> <font color="#F15D22"><b>|</b></font>
<font color="#48B9C7"><b>116</b></font> <font color="#48B9C7"><b>| </b></font><font color="#F15D22"><b>|</b></font> }
<font color="#48B9C7"><b>117</b></font> <font color="#48B9C7"><b>| </b></font><font color="#F15D22"><b>|</b></font> }
<font color="#48B9C7"><b>| </b></font><font color="#F15D22"><b>|_^</b></font>
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>= </b></font><b>note</b>: conflicting implementation in crate `core`:
- impl<T> From<T> for T;
<font color="#F15D22"><b>error</b></font><b>: aborting due to previous error</b>
</pre>
<p>Oh god damnit, fking overlap rule, this says that it the fact that <code class="language-plaintext highlighter-rouge">ErrorMessage</code> impls <code class="language-plaintext highlighter-rouge">Display</code> means we can’t impl <code class="language-plaintext highlighter-rouge">From<Display></code> because then <code class="language-plaintext highlighter-rouge">ErrorMessage::from(error_message)</code> wouldn’t know if it should do an identity conversion (just immediately return the same value) or wrap it inside the message field of a new <code class="language-plaintext highlighter-rouge">ErrorMessage</code>.</p>
<p>…, Fuck it! We don’t need to impl <code class="language-plaintext highlighter-rouge">Display</code> here, its an internal type, and we already have an identical <code class="language-plaintext highlighter-rouge">Debug</code> impl, buahahahaha.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Display</span> <span class="k">for</span> <span class="n">Error</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">fmt</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">,</span> <span class="n">f</span><span class="p">:</span> <span class="o">&</span><span class="k">mut</span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Formatter</span><span class="o"><</span><span class="nv">'_</span><span class="o">></span><span class="p">)</span> <span class="k">-></span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Result</span> <span class="p">{</span>
<span class="k">use</span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Debug</span><span class="p">;</span>
<span class="k">self</span><span class="py">.message</span><span class="nf">.fmt</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c">// impl fmt::Display for ErrorMessage {</span>
<span class="c">// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {</span>
<span class="c">// self.message.fmt(f)</span>
<span class="c">// }</span>
<span class="c">// }</span>
</code></pre></div></div>
<p><img src="/assets/trait_palpatine.png" alt="yes, yes... let the trait flow through you" /></p>
<p><code class="language-plaintext highlighter-rouge">cargo</code>:</p>
<pre><font color="#4E9A06"><b>❯</b></font> cargo +nightly run --example report
<font color="#4E9A06"><b> Compiling</b></font> playground v0.1.0 (/home/jlusby/playground)
<font color="#C4A000"><b>warning</b></font><b>: unused variable: `msg`</b>
<font color="#48B9C7"><b>--> </b></font>src/lib.rs:81:26
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>81</b></font> <font color="#48B9C7"><b>| </b></font> fn wrap_err<D>(self, msg: D) -> Self::Return
<font color="#48B9C7"><b>| </b></font> <font color="#C4A000"><b>^^^</b></font> <font color="#C4A000"><b>help: if this is intentional, prefix it with an underscore: `_msg`</b></font>
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>= </b></font><b>note</b>: `#[warn(unused_variables)]` on by default
<font color="#C4A000"><b>warning</b></font><b>: unused variable: `msg`</b>
<font color="#48B9C7"><b>--> </b></font>src/lib.rs:95:26
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>95</b></font> <font color="#48B9C7"><b>| </b></font> fn wrap_err<D>(self, msg: D) -> Self::Return
<font color="#48B9C7"><b>| </b></font> <font color="#C4A000"><b>^^^</b></font> <font color="#C4A000"><b>help: if this is intentional, prefix it with an underscore: `_msg`</b></font>
<font color="#C4A000"><b>warning</b></font><b>: 2 warnings emitted</b>
<font color="#4E9A06"><b> Finished</b></font> dev [unoptimized + debuginfo] target(s) in 0.37s
<font color="#4E9A06"><b> Running</b></font> `target/debug/examples/report`
thread 'main' panicked at 'not yet implemented', src/lib.rs:99:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
</pre>
<p>Sick, we’re in the final stretch, just gotta fill in the <code class="language-plaintext highlighter-rouge">WrapErr</code> impls and I think we’re good to go, this one will be a little tricky, but it should be fine with our handy dandy friend <code class="language-plaintext highlighter-rouge">std::mem::swap</code>.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span> <span class="n">WrapErr</span> <span class="k">for</span> <span class="n">Error</span> <span class="p">{</span>
<span class="k">type</span> <span class="n">Return</span> <span class="o">=</span> <span class="n">Error</span><span class="p">;</span>
<span class="k">fn</span> <span class="n">wrap_err</span><span class="o"><</span><span class="n">D</span><span class="o">></span><span class="p">(</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="n">msg</span><span class="p">:</span> <span class="n">D</span><span class="p">)</span> <span class="k">-></span> <span class="nn">Self</span><span class="p">::</span><span class="n">Return</span>
<span class="k">where</span>
<span class="n">D</span><span class="p">:</span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Display</span> <span class="o">+</span> <span class="nb">Send</span> <span class="o">+</span> <span class="n">Sync</span> <span class="o">+</span> <span class="nv">'static</span><span class="p">,</span>
<span class="p">{</span>
<span class="k">let</span> <span class="k">mut</span> <span class="n">message</span> <span class="o">=</span> <span class="n">ErrorMessage</span> <span class="p">{</span>
<span class="n">message</span><span class="p">:</span> <span class="nn">Box</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">msg</span><span class="p">),</span>
<span class="n">source</span><span class="p">:</span> <span class="nb">None</span><span class="p">,</span>
<span class="p">};</span>
<span class="nn">std</span><span class="p">::</span><span class="nn">mem</span><span class="p">::</span><span class="nf">swap</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="k">self</span><span class="py">.message</span><span class="p">,</span> <span class="o">&</span><span class="k">mut</span> <span class="n">message</span><span class="p">);</span>
<span class="k">self</span><span class="py">.message.source</span> <span class="o">=</span> <span class="nf">Some</span><span class="p">(</span><span class="nn">Box</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">message</span><span class="p">));</span>
<span class="k">self</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">impl</span><span class="o"><</span><span class="n">T</span><span class="p">,</span> <span class="n">E</span><span class="o">></span> <span class="n">WrapErr</span> <span class="k">for</span> <span class="n">Result</span><span class="o"><</span><span class="n">T</span><span class="p">,</span> <span class="n">E</span><span class="o">></span>
<span class="k">where</span>
<span class="n">E</span><span class="p">:</span> <span class="n">Into</span><span class="o"><</span><span class="n">Error</span><span class="o">></span><span class="p">,</span>
<span class="p">{</span>
<span class="k">type</span> <span class="n">Return</span> <span class="o">=</span> <span class="n">Result</span><span class="o"><</span><span class="n">T</span><span class="p">,</span> <span class="n">Error</span><span class="o">></span><span class="p">;</span>
<span class="k">fn</span> <span class="n">wrap_err</span><span class="o"><</span><span class="n">D</span><span class="o">></span><span class="p">(</span><span class="k">self</span><span class="p">,</span> <span class="n">msg</span><span class="p">:</span> <span class="n">D</span><span class="p">)</span> <span class="k">-></span> <span class="nn">Self</span><span class="p">::</span><span class="n">Return</span>
<span class="k">where</span>
<span class="n">D</span><span class="p">:</span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Display</span> <span class="o">+</span> <span class="nb">Send</span> <span class="o">+</span> <span class="n">Sync</span> <span class="o">+</span> <span class="nv">'static</span><span class="p">,</span>
<span class="p">{</span>
<span class="k">self</span><span class="nf">.map_err</span><span class="p">(|</span><span class="n">e</span><span class="p">|</span> <span class="p">{</span>
<span class="k">let</span> <span class="n">e</span> <span class="o">=</span> <span class="n">e</span><span class="nf">.into</span><span class="p">();</span>
<span class="n">e</span><span class="nf">.wrap_err</span><span class="p">(</span><span class="n">msg</span><span class="p">)</span>
<span class="p">})</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>… Is that it? I think we’re done :O</p>
<p>Lets see what we got</p>
<p><code class="language-plaintext highlighter-rouge">cargo</code>:</p>
<pre><font color="#4E9A06"><b>❯</b></font> cargo +nightly run --example report
<font color="#4E9A06"><b> Compiling</b></font> playground v0.1.0 (/home/jlusby/playground)
<font color="#4E9A06"><b> Finished</b></font> dev [unoptimized + debuginfo] target(s) in 0.30s
<font color="#4E9A06"><b> Running</b></font> `target/debug/examples/report`
Error: this error should not be ignored
Location:
examples/report.rs:12:68
</pre>
<p>Hmm, nope, we’re missing something… Oh yea, we forgot to return our source in the <code class="language-plaintext highlighter-rouge">Error</code> trait, I swear I always do this…</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span> <span class="nn">std</span><span class="p">::</span><span class="nn">error</span><span class="p">::</span><span class="n">Error</span> <span class="k">for</span> <span class="n">Error</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">source</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">)</span> <span class="k">-></span> <span class="nb">Option</span><span class="o"><&</span><span class="p">(</span><span class="n">dyn</span> <span class="nn">std</span><span class="p">::</span><span class="nn">error</span><span class="p">::</span><span class="n">Error</span> <span class="o">+</span> <span class="nv">'static</span><span class="p">)</span><span class="o">></span> <span class="p">{</span>
<span class="k">self</span><span class="py">.message</span><span class="nf">.source</span><span class="p">()</span>
<span class="p">}</span>
<span class="k">fn</span> <span class="nf">backtrace</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">)</span> <span class="k">-></span> <span class="nb">Option</span><span class="o"><&</span><span class="n">Backtrace</span><span class="o">></span> <span class="p">{</span>
<span class="nf">Some</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="py">.backtrace</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p><code class="language-plaintext highlighter-rouge">rustc</code>:</p>
<pre><font color="#4E9A06"><b>❯</b></font> cargo +nightly run --example report
<font color="#4E9A06"><b> Compiling</b></font> playground v0.1.0 (/home/jlusby/playground)
<font color="#F15D22"><b>error[E0599]</b></font><b>: no method named `source` found for struct `ErrorMessage` in the current scope</b>
<font color="#48B9C7"><b>--> </b></font>src/lib.rs:32:22
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>32</b></font> <font color="#48B9C7"><b>| </b></font> self.message.source()
<font color="#48B9C7"><b>| </b></font> <font color="#F15D22"><b>^^^^^^</b></font><font color="#48B9C7"><b>--</b></font> <font color="#48B9C7"><b>help: remove the arguments</b></font>
<font color="#48B9C7"><b>| </b></font> <font color="#F15D22"><b>|</b></font>
<font color="#48B9C7"><b>| </b></font> <font color="#F15D22"><b>field, not a method</b></font>
<font color="#48B9C7"><b>...</b></font>
<font color="#48B9C7"><b>119</b></font> <font color="#48B9C7"><b>| </b></font>struct ErrorMessage {
<font color="#48B9C7"><b>| -------------------</b></font> <font color="#48B9C7"><b>method `source` not found for this</b></font>
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>= </b></font><b>help</b>: items from traits can only be used if the trait is implemented and in scope
<font color="#48B9C7"><b>= </b></font><b>note</b>: the following trait defines an item `source`, perhaps you need to implement it:
candidate #1: `std::error::Error`
<font color="#F15D22"><b>error</b></font><b>: aborting due to previous error</b>
</pre>
<p>Oh yea, gotta impl <code class="language-plaintext highlighter-rouge">Error</code> on <code class="language-plaintext highlighter-rouge">ErrorMessage</code> if I want to be able to make the previous error messages available in the chain of <code class="language-plaintext highlighter-rouge">source</code> errors, awe shit I know where this is going…</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span> <span class="nn">std</span><span class="p">::</span><span class="nn">error</span><span class="p">::</span><span class="n">Error</span> <span class="k">for</span> <span class="n">ErrorMessage</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">source</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">)</span> <span class="k">-></span> <span class="nb">Option</span><span class="o"><&</span><span class="p">(</span><span class="n">dyn</span> <span class="nn">std</span><span class="p">::</span><span class="nn">error</span><span class="p">::</span><span class="n">Error</span> <span class="o">+</span> <span class="nv">'static</span><span class="p">)</span><span class="o">></span> <span class="p">{</span>
<span class="k">self</span><span class="py">.source</span><span class="nf">.as_ref</span><span class="p">()</span><span class="nf">.map</span><span class="p">(|</span><span class="n">e</span><span class="p">|</span> <span class="n">e</span> <span class="k">as</span> <span class="mi">_</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Sigh, okay rustc, let me have it:</p>
<p><code class="language-plaintext highlighter-rouge">rustc</code>:</p>
<pre><font color="#4E9A06"><b>❯</b></font> cargo +nightly run --example report
<font color="#4E9A06"><b> Compiling</b></font> playground v0.1.0 (/home/jlusby/playground)
<font color="#F15D22"><b>error[E0277]</b></font><b>: `ErrorMessage` doesn't implement `std::fmt::Display`</b>
<font color="#48B9C7"><b>--> </b></font>src/lib.rs:136:6
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>136</b></font> <font color="#48B9C7"><b>| </b></font>impl std::error::Error for ErrorMessage {
<font color="#48B9C7"><b>| </b></font> <font color="#F15D22"><b>^^^^^^^^^^^^^^^^^</b></font> <font color="#F15D22"><b>`ErrorMessage` cannot be formatted with the default formatter</b></font>
<font color="#48B9C7"><b>| </b></font>
<font color="#48B9C7"><b>::: </b></font>/home/jlusby/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/error.rs:48:26
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>48</b></font> <font color="#48B9C7"><b>| </b></font>pub trait Error: Debug + Display {
<font color="#48B9C7"><b>| </b></font> <font color="#48B9C7"><b>-------</b></font> <font color="#48B9C7"><b>required by this bound in `std::error::Error`</b></font>
<font color="#48B9C7"><b>|</b></font>
<font color="#48B9C7"><b>= </b></font><b>help</b>: the trait `std::fmt::Display` is not implemented for `ErrorMessage`
<font color="#48B9C7"><b>= </b></font><b>note</b>: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
</pre>
<p>Sad face, bitten in the ass by my own cleverness. I guess I’ll just manually impl <code class="language-plaintext highlighter-rouge">From</code> for Kind only :(((</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span> <span class="n">From</span><span class="o"><</span><span class="n">Kind</span><span class="o">></span> <span class="k">for</span> <span class="n">ErrorMessage</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">from</span><span class="p">(</span><span class="n">kind</span><span class="p">:</span> <span class="n">Kind</span><span class="p">)</span> <span class="k">-></span> <span class="n">Self</span> <span class="p">{</span>
<span class="n">ErrorMessage</span> <span class="p">{</span>
<span class="n">message</span><span class="p">:</span> <span class="nn">Box</span><span class="p">::</span><span class="nf">new</span><span class="p">(</span><span class="n">kind</span><span class="p">),</span>
<span class="n">source</span><span class="p">:</span> <span class="nb">None</span><span class="p">,</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">impl</span> <span class="nn">std</span><span class="p">::</span><span class="nn">error</span><span class="p">::</span><span class="n">Error</span> <span class="k">for</span> <span class="n">ErrorMessage</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">source</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">)</span> <span class="k">-></span> <span class="nb">Option</span><span class="o"><&</span><span class="p">(</span><span class="n">dyn</span> <span class="nn">std</span><span class="p">::</span><span class="nn">error</span><span class="p">::</span><span class="n">Error</span> <span class="o">+</span> <span class="nv">'static</span><span class="p">)</span><span class="o">></span> <span class="p">{</span>
<span class="k">self</span><span class="py">.source</span><span class="nf">.as_ref</span><span class="p">()</span><span class="nf">.map</span><span class="p">(|</span><span class="n">e</span><span class="p">|</span> <span class="n">e</span> <span class="k">as</span> <span class="mi">_</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">impl</span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Debug</span> <span class="k">for</span> <span class="n">ErrorMessage</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">fmt</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">,</span> <span class="n">f</span><span class="p">:</span> <span class="o">&</span><span class="k">mut</span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Formatter</span><span class="o"><</span><span class="nv">'_</span><span class="o">></span><span class="p">)</span> <span class="k">-></span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Result</span> <span class="p">{</span>
<span class="k">self</span><span class="py">.message</span><span class="nf">.fmt</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">impl</span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Display</span> <span class="k">for</span> <span class="n">ErrorMessage</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">fmt</span><span class="p">(</span><span class="o">&</span><span class="k">self</span><span class="p">,</span> <span class="n">f</span><span class="p">:</span> <span class="o">&</span><span class="k">mut</span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Formatter</span><span class="o"><</span><span class="nv">'_</span><span class="o">></span><span class="p">)</span> <span class="k">-></span> <span class="nn">fmt</span><span class="p">::</span><span class="n">Result</span> <span class="p">{</span>
<span class="k">self</span><span class="py">.message</span><span class="nf">.fmt</span><span class="p">(</span><span class="n">f</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Okay, this time I think we got it…</p>
<pre><font color="#4E9A06"><b>❯</b></font> cargo +nightly run --example report
<font color="#4E9A06"><b> Compiling</b></font> playground v0.1.0 (/home/jlusby/playground)
<font color="#4E9A06"><b> Finished</b></font> dev [unoptimized + debuginfo] target(s) in 0.35s
<font color="#4E9A06"><b> Running</b></font> `target/debug/examples/report`
Error: this error should not be ignored
Caused by:
this error was important to us
Location:
examples/report.rs:12:68
</pre>
<p>Fuck yea! Nailed it, I’m the best ^_^</p>
<h2 id="conclusion">Conclusion</h2>
<p>Okay so that’s how I write error types, I ended up writing a recreation of the dev process by literally just doing it again from scratch. I hope you all enjoy reading it as much as I enjoyed half assing writing it.</p>
<h2 id="extras">Extras</h2>
<p>There are a few features here that could be added easily that I didn’t go ahead and do in this case. For example, <code class="language-plaintext highlighter-rouge">eyre</code> and <code class="language-plaintext highlighter-rouge">anyhow</code> both occupy only one pointer on the stack, where as this <code class="language-plaintext highlighter-rouge">Error</code> kind is rather chonky in comparison, with the multiple Boxes in <code class="language-plaintext highlighter-rouge">ErrorMessage</code> and the <code class="language-plaintext highlighter-rouge">Backtrace</code>. It’s not much harder to make the <code class="language-plaintext highlighter-rouge">Error</code> type defined here into a private <code class="language-plaintext highlighter-rouge">ErrorImpl</code> and then make a new <code class="language-plaintext highlighter-rouge">struct Error(Box<ErrorImpl>);</code> type to wrap it. This will help with happy path code by keeping stack frames smaller, but adds an extra allocation when errors are actually thrown, its up to you to decide which you need more.</p>
<p>Another thing, in this example I only ever showed creating <code class="language-plaintext highlighter-rouge">Errors</code> from <code class="language-plaintext highlighter-rouge">Kind</code>, but you could add other <code class="language-plaintext highlighter-rouge">From</code> impls for <code class="language-plaintext highlighter-rouge">Error</code>. Say you have a lot of random errors that aren’t important to handle, you could add something like this:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">impl</span> <span class="n">From</span><span class="o"><&</span><span class="nv">'static</span> <span class="nb">str</span><span class="o">></span> <span class="k">for</span> <span class="n">Error</span> <span class="p">{</span>
<span class="k">fn</span> <span class="nf">from</span><span class="p">(</span><span class="n">msg</span><span class="p">:</span> <span class="o">&</span><span class="nv">'static</span> <span class="nb">str</span><span class="p">)</span> <span class="k">-></span> <span class="n">Error</span> <span class="p">{</span>
<span class="n">Error</span> <span class="p">{</span>
<span class="n">kind</span><span class="p">:</span> <span class="nn">Kind</span><span class="p">::</span><span class="n">Other</span><span class="p">,</span>
<span class="n">backtrace</span><span class="p">:</span> <span class="nn">Backtrace</span><span class="p">::</span><span class="nf">capture</span><span class="p">(),</span>
<span class="n">message</span><span class="p">:</span> <span class="nn">ErrorMessage</span><span class="p">::</span><span class="nf">from</span><span class="p">(</span><span class="n">msg</span><span class="p">),</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Get creative with it, you’ll probably run into some of the same <code class="language-plaintext highlighter-rouge">fmt::Display</code> overlap errors here so you’ll have to implement the from impls for each individual type you want to use as a source, which you can deduplicate with macros if you end up doing with many different types.</p>
<p>Thats all I’ve got for now. If you’ve got a similar problem where you don’t know how to write just the right error handling type for your library or application please feel free to nerd snipe me. I’ll love to do this again and get a Part 2 up, and so on. Take care everyone!</p>I know this blog post looks long but I swear its mostly snippets of rustc errors that you can just skim past.Customizing Error Reporting via Hooks2020-08-20T10:30:00+00:002020-08-20T10:30:00+00:00https://yaah.dev/custom-report-hooks<h1 id="coming-soon">Coming Soon</h1>
<p>I’m sorry, I know I promised a blog post about this in my talk but 2020 has gotten the best of me, I’ll hopefully have it done some time within the next week or two.</p>Coming SoonSymme(try blocks)2020-04-09T00:00:00+00:002020-04-09T00:00:00+00:00https://yaah.dev/try-blocks<h2 id="introduction">Introduction</h2>
<p>For the last few days the rust community has been abuzz with renewed
discussions about <code class="language-plaintext highlighter-rouge">try</code> syntax in response to a series of blog
posts(<a href="https://boats.gitlab.io/blog/post/failure-to-fehler/">[1]</a><a href="https://boats.gitlab.io/blog/post/why-ok-wrapping/">[2]</a>)
by <a href="https://twitter.com/withoutboats">@withoutboats</a>. This has triggered some
fairly intense bikeshedding and vitriolic objections and, it seems, some
progress!</p>
<p>As of yesterday the lang team is moving forward with a
<a href="https://github.com/rust-lang/rust/issues/70941">proposal</a> to resolve issues
with <code class="language-plaintext highlighter-rouge">try</code> blocks, and presumably, begin stabilizing them. It seems that the
lang team is arriving at consensus to merge try blocks, where as historically
this has been blocked on disagreements about ok-wrapping. So what changed? Two
things, first, it became apparent that try blocks and ok wrapping could be
separated from proposals for try functions and <code class="language-plaintext highlighter-rouge">throws</code> syntax. And second, <a href="https://www.reddit.com/r/rust/comments/fw4jsx/from_failure_to_fehler/fmmtt7o/">a
comment</a>
on reddit compellingly compared try and async blocks as similar effects
<em>applied</em> to blocks.</p>
<h2 id="background">Background</h2>
<p>Currently, fallibility in rust is expressed with the Result enum. This works
like any other enum, in that you have to construct each variant manually, with
results this is known as Ok-wrapping.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">foo</span><span class="p">()</span> <span class="k">-></span> <span class="n">Result</span><span class="o"><</span><span class="n">PathBuf</span><span class="p">,</span> <span class="nn">io</span><span class="p">::</span><span class="n">Error</span><span class="o">></span> <span class="p">{</span>
<span class="k">let</span> <span class="n">base</span> <span class="o">=</span> <span class="nn">env</span><span class="p">::</span><span class="nf">current_dir</span><span class="p">()</span><span class="o">?</span><span class="p">;</span>
<span class="nf">Ok</span><span class="p">(</span><span class="n">base</span><span class="nf">.join</span><span class="p">(</span><span class="s">"foo"</span><span class="p">))</span>
<span class="p">}</span>
</code></pre></div></div>
<p>For a while now there have been proposals to introduce fallible functions, also
known as try functions or throws syntax. These proposals have historically
looked a little like this:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">foo</span><span class="p">()</span> <span class="k">-></span> <span class="n">PathBuf</span> <span class="n">throws</span> <span class="nn">io</span><span class="p">::</span><span class="n">Error</span> <span class="p">{</span>
<span class="k">let</span> <span class="n">base</span> <span class="o">=</span> <span class="nn">env</span><span class="p">::</span><span class="nf">current_dir</span><span class="p">()</span><span class="o">?</span><span class="p">;</span>
<span class="n">base</span><span class="nf">.join</span><span class="p">(</span><span class="s">"foo"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="separating-ok-wrapping-from-try-functions">Separating Ok-Wrapping From Try Functions</h2>
<p>Ok-wrapping and try functions are often bundled together in proposals about
either, but this doesnt have to be the case. In fact, on nightly rust it’s been
possible to enable try <em>blocks</em> (with Ok-wrapping) for a while. You can go on
<a href="https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=347d8f346dff1fc29273aa436421ea3c">nightly right
now</a>
and write the previous example like this:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nd">#![feature(try_blocks)]</span>
<span class="k">fn</span> <span class="nf">foo</span><span class="p">()</span> <span class="k">-></span> <span class="n">Result</span><span class="o"><</span><span class="n">PathBuf</span><span class="p">,</span> <span class="nn">io</span><span class="p">::</span><span class="n">Error</span><span class="o">></span> <span class="p">{</span>
<span class="n">try</span> <span class="p">{</span>
<span class="k">let</span> <span class="n">base</span> <span class="o">=</span> <span class="nn">env</span><span class="p">::</span><span class="nf">current_dir</span><span class="p">()</span><span class="o">?</span><span class="p">;</span>
<span class="n">base</span><span class="nf">.join</span><span class="p">(</span><span class="s">"foo"</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>It turns out that separating the two proposals from each other allowed the
language team to come to consensus on try blocks and narrow the disagreements
to just function-level try. It’s not the syntax sugar of wrapping returns that
blocked the try block proposal, it was the idea that this would lead
immediately to Result being stripped from the return type. And, when you dig
deeper into ok-wrapping as a block level effect, it starts to lead to some
attractive symmetry in the language.</p>
<h2 id="comparison-to-async">Comparison To Async</h2>
<p>The previously mentioned
<a href="https://www.reddit.com/r/rust/comments/fw4jsx/from_failure_to_fehler/fmmtt7o/">comment</a>
does a much better job explaining this than I can but it boils down to this
snippet:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="n">try</span> <span class="p">{</span> <span class="n">x</span><span class="o">?</span> <span class="p">}</span> <span class="o">==</span> <span class="n">x</span> <span class="o">==</span> <span class="p">(</span><span class="n">try</span><span class="p">{</span> <span class="n">x</span> <span class="p">})</span><span class="o">?</span>
<span class="k">async</span> <span class="p">{</span> <span class="n">x</span><span class="k">.await</span> <span class="p">}</span> <span class="o">==</span> <span class="n">x</span> <span class="o">==</span> <span class="p">(</span><span class="k">async</span><span class="p">{</span> <span class="n">x</span> <span class="p">})</span><span class="k">.await</span>
</code></pre></div></div>
<p>The idea is that <code class="language-plaintext highlighter-rouge">async {}</code> and <code class="language-plaintext highlighter-rouge">await</code> cancel eachother out, and the same is
true for <code class="language-plaintext highlighter-rouge">try {}</code> and <code class="language-plaintext highlighter-rouge">?</code>. When framed this way you can start to see <code class="language-plaintext highlighter-rouge">try</code> and
<code class="language-plaintext highlighter-rouge">async</code> as effects applied to blocks, rather than seeing them as just <code class="language-plaintext highlighter-rouge">try
blocks</code> or <code class="language-plaintext highlighter-rouge">async blocks</code>.</p>
<h2 id="what-id-like-to-see-next">What I’d Like to See Next</h2>
<p>To start we gotta stabilize try blocks. This is already in progress so there’s
not much to add on this point other than bikeshedding which keyword, <code class="language-plaintext highlighter-rouge">raise</code>,
<code class="language-plaintext highlighter-rouge">pass</code>, <code class="language-plaintext highlighter-rouge">fail</code>, <code class="language-plaintext highlighter-rouge">throw</code>, or <code class="language-plaintext highlighter-rouge">yeet</code> should be used to return errors within the
try block. And I dont want to get into that, it is not the point of this post.</p>
<p>However, once we have try blocks I imagine I’m not going to love having to put
a try block and indent level around the full body of every function that
returns a Result. To deal with this I would like to propose adding support for
annotating the function body block itself with <code class="language-plaintext highlighter-rouge">try</code>. This is distinct from
function level try, which to me is the try equivalent of <code class="language-plaintext highlighter-rouge">async fn</code>, and would
be written as <code class="language-plaintext highlighter-rouge">try fn</code>. Also, these proposals are not mutually exclusive. With
this change the above example becomes:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">foo</span><span class="p">()</span> <span class="k">-></span> <span class="n">Result</span><span class="o"><</span><span class="n">PathBuf</span><span class="p">,</span> <span class="nn">io</span><span class="p">::</span><span class="n">Error</span><span class="o">></span> <span class="n">try</span> <span class="p">{</span>
<span class="k">let</span> <span class="n">base</span> <span class="o">=</span> <span class="nn">env</span><span class="p">::</span><span class="nf">current_dir</span><span class="p">()</span><span class="o">?</span><span class="p">;</span>
<span class="n">base</span><span class="nf">.join</span><span class="p">(</span><span class="s">"foo"</span><span class="p">)</span>
<span class="p">}</span>
</code></pre></div></div>
<p>We’ve essentially just removed a redundant block around the function body. Now
we already get one of the major benefits of withoutboats’ proposal, we don’t
have to edit every return location to wrap it with a Result when we introduce
an error return channel. However the Result is still visible, and the function
signature is effectively unchanged. You can think of the function signature as
outside the “Result monad” or “try effect” while the function body block itself
is inside.</p>
<p>Going back to the comparison for async, I don’t see why we couldn’t extend this
change to also apply to <code class="language-plaintext highlighter-rouge">async</code> blocks. I can’t think of any particular reasons
why this would be desirable other than language consistency, but given the
intensity of the discussion over whether or not async fns should hide the
<code class="language-plaintext highlighter-rouge">Future</code> from the return type I’m guessing there are at least some reasons to
allow this.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">async</span> <span class="k">fn</span> <span class="nf">foo</span><span class="p">()</span> <span class="k">-></span> <span class="nb">i32</span> <span class="p">{</span>
<span class="nf">bar</span><span class="p">()</span><span class="k">.await</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Could be also then be written as:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">foo</span><span class="p">()</span> <span class="k">-></span> <span class="k">impl</span> <span class="n">Future</span><span class="o"><</span><span class="n">Output</span> <span class="o">=</span> <span class="nb">i32</span><span class="o">></span> <span class="k">async</span> <span class="p">{</span>
<span class="nf">bar</span><span class="p">()</span><span class="k">.await</span>
<span class="p">}</span>
</code></pre></div></div>
<p>In this version of rust <code class="language-plaintext highlighter-rouge">async</code> and <code class="language-plaintext highlighter-rouge">try</code> would effectively become block
effects and act like <code class="language-plaintext highlighter-rouge">do</code> notation for their respective monads. Now, we could
stop here, and only allow block effects in places where you couldn’t already put
an expression, function body blocks and bare blocks. Or we can go for maximum
consistency and allow block effects in any expression with a block:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">d</span> <span class="o">=</span> <span class="k">if</span> <span class="n">s</span><span class="nf">.is_empty</span><span class="p">()</span> <span class="n">try</span> <span class="p">{</span>
<span class="k">let</span> <span class="n">a</span> <span class="o">=</span> <span class="n">b</span><span class="o">?</span><span class="p">;</span>
<span class="n">a</span> <span class="o">+</span> <span class="n">c</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="nf">Err</span><span class="p">(</span><span class="o">...</span><span class="p">)</span>
<span class="p">}</span><span class="o">?</span><span class="p">;</span>
<span class="c">// Already valid</span>
<span class="k">let</span> <span class="mi">_</span> <span class="o">=</span> <span class="p">||</span> <span class="n">try</span> <span class="p">{</span> <span class="o">...</span> <span class="p">};</span>
<span class="k">let</span> <span class="mi">_</span> <span class="p">||</span> <span class="k">async</span> <span class="p">{</span> <span class="o">...</span> <span class="p">};</span>
<span class="k">match</span> <span class="n">foo</span> <span class="n">try</span> <span class="p">{</span>
<span class="o">...</span>
<span class="p">}</span>
<span class="k">let</span> <span class="n">c</span> <span class="o">=</span> <span class="k">loop</span> <span class="n">try</span> <span class="p">{</span>
<span class="o">...</span>
<span class="p">}</span><span class="o">?</span><span class="p">;</span>
</code></pre></div></div>
<p>We could do the same thing with unsafe, and treat it like a block effect. Fun
fact, rust is currently moving away from unsafe functions having an <a href="https://github.com/rust-lang/rfcs/pull/2585">unsafe
body block</a> by default, and this
generalization could go hand in hand with that change.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// with new rfc body is safe by default</span>
<span class="k">unsafe</span> <span class="k">fn</span> <span class="nf">foo</span><span class="p">()</span> <span class="p">{</span>
<span class="o">...</span>
<span class="p">}</span>
<span class="c">// new way to get an entirely unsafe body</span>
<span class="k">unsafe</span> <span class="k">fn</span> <span class="nf">foo</span><span class="p">()</span> <span class="p">{</span>
<span class="k">unsafe</span> <span class="p">{</span>
<span class="o">...</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c">// same unsafe body with generalized block effects</span>
<span class="k">unsafe</span> <span class="k">fn</span> <span class="nf">foo</span><span class="p">()</span> <span class="k">unsafe</span> <span class="p">{</span>
<span class="o">...</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Or even treat expressions like <code class="language-plaintext highlighter-rouge">if</code>, <code class="language-plaintext highlighter-rouge">match</code> and <code class="language-plaintext highlighter-rouge">loop</code> as block effects as well:</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">fn</span> <span class="nf">foo</span><span class="p">(</span><span class="n">bar</span><span class="p">:</span> <span class="n">Bar</span><span class="p">)</span> <span class="k">-></span> <span class="n">Baz</span> <span class="k">match</span> <span class="n">bar</span> <span class="p">{</span>
<span class="nn">Bar</span><span class="p">::</span><span class="nf">Quix</span><span class="p">(</span><span class="n">q</span><span class="p">)</span> <span class="k">=></span> <span class="o">...</span><span class="p">,</span>
<span class="o">...</span>
<span class="p">}</span>
<span class="k">for</span> <span class="n">bar</span> <span class="n">in</span> <span class="n">bars</span> <span class="k">match</span> <span class="n">bar</span> <span class="p">{</span>
<span class="nn">Bar</span><span class="p">::</span><span class="nf">Quix</span><span class="p">(</span><span class="n">q</span><span class="p">)</span> <span class="k">=></span> <span class="o">...</span><span class="p">,</span>
<span class="o">...</span>
<span class="p">}</span>
<span class="k">if</span> <span class="n">is_empty</span> <span class="k">loop</span> <span class="n">try</span> <span class="p">{</span>
<span class="p">};</span>
<span class="c">// these examples could go on for QUITE a while...</span>
</code></pre></div></div>
<p>We treat the keywords in <code class="language-plaintext highlighter-rouge">fn</code>, <code class="language-plaintext highlighter-rouge">if</code>, <code class="language-plaintext highlighter-rouge">match</code>, <code class="language-plaintext highlighter-rouge">loop</code>, <code class="language-plaintext highlighter-rouge">async</code>, <code class="language-plaintext highlighter-rouge">unsafe</code>, and
<code class="language-plaintext highlighter-rouge">try</code> as composable effects applied to blocks. This lets us concisely and
intuitively compose types and control-flow via syntax sugar.</p>
<p>And, the nice thing about this symmetry is that it helps neutralize a lot of
people’s concerns around rust getting “too big”. We’re not making it bigger,
we’re making it more consistent!</p>
<p>Finally, this leaves room for discussions over whether or not there should also be a
<code class="language-plaintext highlighter-rouge">try</code> effect equivalent to <code class="language-plaintext highlighter-rouge">async fn</code>, where the function definition itself
also exists within the monad, similar to the original <code class="language-plaintext highlighter-rouge">throws function</code>
proposals. And, it remains to be seen whether or not any of this can actually
parse given existing stable syntax, but I hope others agree with me that
this generalization is worth digging into.</p>
<h2 id="conclusion">Conclusion</h2>
<p>With all three of these steps you would be able pick and choose how you want to
handle control flow with Try types. A single keyword enables the effect, and
you have a slew of scopes at which you can apply it. The similarity between
async effects and try effects would make both of them easier to teach. Once
you’re familiar with one of the effects the other should come intuitively.</p>IntroductionNavigating The Rust OSS Community2019-09-12T13:40:00+00:002019-09-12T13:40:00+00:00https://yaah.dev/getting-involved<h1 id="navigating-the-rust-oss-community">Navigating The Rust OSS Community</h1>
<h2 id="disclaimer">DISCLAIMER</h2>
<p><strong>This blog post is the textual version of the talk it was written to prepare for (<a href="https://www.youtube.com/watch?v=QKbdBwjra5o">video</a>). The content should be essentially the same, though with less stumbling over my own words, less silly hand gestures, and less crying. Please Enjoy.</strong></p>
<h2 id="preface-expectations">Preface: Expectations</h2>
<p>Are you a new Rustacean trying to take your first steps into Rust’s open source community? Are you trying to meet people in the Rust community but don’t know where to start? Are you a maintainer interested in attracting new contributors? Then have I got the talk for you! Together we will explore some approaches to find issues to work on, seek out new projects, structure projects to help new contributors and meet other Rust developers to become happy and succcessful contributors in the Rust open source community.</p>
<p>Okay, Cheesy informercial-shtick aside, why am I up here? I’m here to talk about getting involved in the Rust open source community. My goal is to help new contributors and established maintainers alike. Hopefully by the end of this talk you will have a better idea of how to navigate our community and know how to best help those around you navigate it as well.</p>
<p>One thing this talk is not is a guide on how to learn Rust, the language. I’m not going to talk about how I learned the language or give any tips on how to improve your Rust skills directly. There are many other wonderful resources out there for how to learn rust. I’m here to help you find those resources, not to be one of them. This talk is about the community side of rust, not the technical side.</p>
<p>So A little background about myself. My name is Jane Lusby, I’m a software engineer working on distributed storage at a virtualization company called “Scale Computing”. I work professionally on C++ and have for about 6 years now. I was introduced to Rust in the beginning of 2018 at my work’s maker night, I fell in love, and quickly decided that I wanted to get involved in the community at large, give back, learn more, etc. Now, I’d wanted to get involved in open source for a long time, but with other languages it had never really clicked and I’d never managed to get involved.</p>
<p>So what did rust do differently that actually got me involved where other communities had failed? I’m glad you asked…</p>
<h1 id="1-code-of-conduct">1. Code of Conduct</h1>
<p>One of the first things I saw when I looked into Rust was its Code of Conduct. Now let me give you a little extra background about myself, I’m a woman, I’m gay, and I’m transgender. When I go looking at participating in a community misogyny, transphobia, and homophobia are all things I worry about.</p>
<p>So when I looked at the Rust community’s code of conduct and how it banned discrimination on the basis of gender, race, ethnicity, age, disability, and other similar attributes, it was like a huge weight had lifted off my shoulders. It’s wonderful to be able to go into a community and know that if you run into a bigot that pretty much everyone has got your back. I didn’t feel afraid to get involved because the code of conduct gave me a sense of safety. Thankfully I’ve not had any problems in the last 2 years, and I’m convinced it’s because the CoC as well. The code of conduct acts as preventative medicine and discourages the people its designed to protect against from ever joining your community in the first place. If you want people like me to get involved in your projects just mentioning that you abide by the Rust community’s code of conduct goes a huge way to making us feel comfortable enough to engage.</p>
<p>So the code of conduct got me excited about rust, but it’s not what got me engaged. What was it that actually got me to do my first open source contribution.</p>
<h1 id="2-this-week-in-rust">2. This Week In Rust</h1>
<p>Incase you haven’t heard of it, the Rust community maintains a weekly newsletter of Rustlang news called <a href="https://this-week-in-rust.org/">This Week In Rust</a>. In it you’ll find sections on recent blog posts, the crate of the week, call for participation, changes merged to core, upcoming meetups, job postings, and usually a funny little quote at the end.</p>
<p>I came to TWIR because of the blog posts, I was obsessed with rust and wanted to absorb every bit of information about it that I could, but the section that ended up having the greatest impact was the Call for Participation section.</p>
<p>The CFP section of TWIR is a list of good first issues and issues that maintainers need extra help on. As I said earlier, I’d wanted to contribute to open source for years but I had never succeeded in finding an opportunity that actually got me to engage. I think the main issue here is that my ADHD brain makes it hard to dive into the unknown without a strong motivation, “later” it always says.</p>
<p>Then along comes TWIR, just waving the flag of friendship weekly. “Heres something you can do to help” it would say to me. The weekly list of friendly first issues I could get involved in made it trivial to find issues that were available to work on and approachable for someone new to the code base. I didn’t have to do anything now to find work, it just walked up to me on its own whenever I went to read the news.</p>
<p>So I got involved, I did a minor issue on <a href="https://github.com/rust-lang/rust/pull/50793">rustc</a>, just cleaning up some test code, and all of a sudden the problem space wasn’t so scary anymore.</p>
<p>One thing I want to note though is that This Week In Rust depends on community participation to function. It used to be the case that the maintainers would go and hunt down a nice set of good first issues for each week, but they stopped doing that a while ago because they didn’t want to unfairly favor any single part of the rust ecosystem. As a result, if you look at old newsletters vs more recent ones, you’ll see that we consistently have far fewer good first issues now adays. If you are a maintainer of any rust open source projects and you’re tagging good first issues and not submitting those issues to TWIR, you should be, it can make all the difference for new rustaceans who are intimidated by the process of getting involved in open source.</p>
<h2 id="21-friction">2.1. Friction</h2>
<p>So I’m no longer scared of the process, I dive in and started trying to find my own new issues to work on from the issue boards, and unfortunately this is where I ran into my first few bit of friction.</p>
<p>As it turns out, github issue boards can be pretty difficult to navigate, https://www.rustaceans.org/findwork/starters, the recommended starting point in TWIR at the time, was particularly hard to work with. It only shows the issue name, summary, and tags. I would go into issue after issue only to find someone else was already working on it. The friction was discouraging, so I switched to only using the github issues lists themselves. There at least I could filter out any issues that had comments in them, which admittedly isnt the best solution, because it’s common for people to open issues on a repo and then for the maintainer to come along and leave a comment with mentorship instructions. I missed all those opportunities due to noise on the issue boards.</p>
<p>The next issue I ran into was maintainer responsiveness. I remember once shyly leaving a comment like “I’d like to take on this feature” and waiting a day or two for a response and getting nothing then quietly deleting my comment and moving on to find a new issue. Now I don’t want to complain here because I think the Rust lang contributors are fantastic people who do great work for little thanks and no pay and I don’t think it’s appropriate to expect work from them. But… that doesn’t change how it feels when you’re trying to get involved and all you hear is crickets.</p>
<p>I ended up settling on a few repos, clippy, rustfix, and cargo, where the maintainers there were particularly responsive and friendly. Shoutout to <a href="https://twitter.com/killercup">@killercup</a>, <a href="https://twitter.com/ManishEarth">@ManishEarth</a>, <a href="https://twitter.com/oli_obk">@oli_obk</a>, <a href="https://twitter.com/philhansch">@phansch</a>, <a href="https://github.com/kennytm">@kennytm</a>, <a href="https://github.com/flip1995">@flip1995</a>, <a href="https://github.com/ehuss">@ehuss</a>, and everyone I’m forgetting for making it so easy to get involved and contribute to your projects.</p>
<h1 id="3-meetups-making-rust-friends">3. Meetups: Making Rust Friends</h1>
<p>The next biggest breakthrough for me came with the discovery of Rust meetups. I had no idea these existed, but a coworker of mine told me I should check out the meetup app and we immediately signed up for the bay area Rustaeceans group and started going to all of the meetups.</p>
<p>For a while nothing really came out of these meetups OSS wise. I would go to meetups and listen to the talks, eat the free food, maybe talk to a person here or there, but I’m really shy around people I don’t know, so I mostly kept to myself or talked with coworkers I’d managed to drag along. I was enjoying myself, and I kept going, but it wasn’t until the meetup at Google’s office that things Really Started Happening.</p>
<p>What happened at the Google meetup you ask? Manish, our wonderful meetup organizer, walked up to me, unprompted, and asked “Hey, you’re Jane right?”. I was shocked, how the heck did Manish know who I was? It didn’t feel as though I’d done anything worthy of notice, and yet here he was asking for me by name. I replied that I was and he thanked me for my contributions to clippy, and told me if I wanted to do more to let him know, and that he would help mentor me. (<em>editors note: this is where I start crying</em>)</p>
<p>This simple act of walking up to me, thanking me for my contributions, and offering to help me do more has without a doubt had a larger impact on my involvement in the Rust community and my life over the last year than any other event in the last two years. I don’t think I’d be here doing this talk, or that I’d have gone to RustConf, or that I’d be giving a talk at COGoldRust, or that I’d even have started using Twitter and met so many more people, if it weren’t for meeting Manish that day.</p>
<p>Now that wasn’t the only thing that happened that day, nor the only person who had a huge impact on me. Eliza Weisman gave a talk on her library <code class="language-plaintext highlighter-rouge">tracing</code> which was super fascinating and extremely relevant to my personal and professional codebases, and Adam Perry invited me to sit with him when I was shyly sitting by myself in the corner after the meetup hoping someone would come talk to me and introduced me to Isabelle Muerte and Rain. Together all of these people essentially became my first “Rust Friends” and meeting them was the first time it really felt like I was part of the Rust community. (<em>end crying</em>)</p>
<p>I ended up tracking Manish down online and sent him a message before the meetup was even over that I was absolutely wanted to get more involved and that I wanted him to be my mentor, and he’s still my Rust mentor today.</p>
<p>The big take away here is that meetup are an amazing way to get involved and to feel involved in a community. There are so many amazing rustaceans that go to all these meetups and conferences, so they’re a great opportunity to meet and learn from amazing people. If you’re not already going to meetups, you should seriously consider starting.</p>
<h1 id="4-mentor">4. Mentor</h1>
<p>So with a new mentor backing me up I went home more inspired than ever to work on open source contributions. Initially, I think I had a bit of a misunderstanding of what a mentor is. My hope for mentorship going into it was to have someone who would find me stuff to work on and help me stay on track. Essentially, a manager. Now Manish did his best to do that for me, but in retrospect it was a lot to ask, being a manager is a job in it’s own right, and the real value of the mentorship Manish gave me is so much more.</p>
<p>One amazing thing he did was be a constant source of encouragement. I remember talking to manish about the <code class="language-plaintext highlighter-rouge">tracing</code> repo and how I wanted to talk to Eliza and get involved, but I was completely intimidated by her and was way too afraid to approach her. Manish on the other hand was like, “No, come on, she’s great, talk to her, she’s super friendly and will be a blast to work with.” He wasn’t wrong. I’ve probably learned more about Rust the language from doing PR work on tracing than I have from everything else in the rust-lang organization combined. I probably wouldn’t have talked to eliza or gotten involved if it weren’t for Manish gently encouraging me to take the leap.</p>
<p>Another thing he did was give constant good advice. I would often go to him to complain about features I wanted that didn’t exist or talk about the bikeshed of the moment, in this case <code class="language-plaintext highlighter-rouge">await</code> syntax. He encouraged me to write a little blog post explaining why I was hoping for postfix macro to be the syntax of choice. I felt silly and underqualified doing it at the time but now, having done it, I no longer feel silly or afraid of writing blogs and I am eternally grateful for that.</p>
<p>Speaking of complaining about features I wanted, Manish fundamentally changed how I approach open source work. Initially my strategy was to go to repos with issues open for other people to work on and just do them. Manish on the other hand encouraged me to take those features I wanted, open issues for them, and implement them instead of random issues. Let me tell you, working on features that you personally want rather than random ones other people want is WAY more satisfying. I’ve found it much easier to be motivated on the features I want rather that ones I ultimately don’t care about. It seems like a more sustainable approach to open source and as a result I’ve contributed to a ton of random repos I use just because I wanted a little feature here or there. It makes me feel warm and fuzzy inside using those features I implemented myself or thinking about those projects and the maintainers I’ve had the pleasure to work with, All thanks to Manish’s mentorship.</p>
<p>I believe that mentors are fundamentally an amplifying force on growth. If you’re a new rustacean trying to get more involved, find yourself a mentor! And if you’re an established rustacean and have the bandwidth, you should seriously consider becoming a mentor. It’s good for the community.</p>
<h2 id="41-mistakes">4.1. Mistakes!</h2>
<p>Unfortunately, with all of this new found motivation I got a bit ahead of myself and suddenly ran into a few more snags. I got a little overzealous and opened 3 prs on cargo at once to add “minor” features that I had been wanting for ages. Turns out a few of them weren’t so minor! I suddenly had this mountain of work facing me that I felt like I’d committed to and it paralyzed me for a couple of weeks. It didn’t seem approachable, and so my ADHD brain went into full avoidance mode. The solution here ended up being to know my limit and to break my tasks up into smaller more approachable tasks. Once I realized, hey I can shelve two of those issues and just work on the one I care most about it was easy to get going again.</p>
<p>Another problem I ran into was organization. I had all of these ideas and features I wanted but I was pretty much just remembering them, talking about them when they came to mind, etc. A fair number of ideas were definitely lost in these early days. Eliza ended up being the one who helped me through this problem. Any time I mentioned a feature I wanted she told me to open an issue, and eventually it became a habit. Bonus points, now people will occasionally pick up issues that I opened for myself and hadn’t yet gotten around to doing. Who doesn’t love free features!</p>
<p>Now that I had all these issues laying around that I wanted to work on I needed a way to keep track of them all. The solution for this ended up being inspired by David Tolnay’s usage of docs.rs as a blog platform. Instead of docs.rs I asked myself if I could use an empty github repo to track my issues. I initially started with a single tracking issue where I would link every other issue, but that notified everyone that I was mentioning their issues so I ended up settling on a project board, which didn’t do that. Now I have a nicely organized list of every issue that integrates seamlessly all the different repos I’m working with and I can share this list with people if they’re looking for things to work on!</p>
<h1 id="5-twitter">5. Twitter</h1>
<p>The next big game changer was learning to love Twitter. Manish and Eliza unintentionally got me into it, I think the main leaping off point was when Manish convinced me to write that await blog post and announce it on Twitter so he could retweet it. I’d never been able to “figure out Twitter” before this but suddenly it clicked and I realized I could use Twitter for Rust! I started to follow every Rustacean I could find and quickly learned to navigate Twitter effectively.</p>
<p>Now, I’ve been an avid Redditor for a solid 10 years, and it has been a great way to keep on top of new emerging features, libraries, and blog posts in the Rust community. Twitter is also a great source for this content, in this I think the platform you pick makes no difference. The real difference is how you interact with people on Twitter vs Reddit. Discussion on Reddit is better for impersonal discussion, where as Twitter tends to encourage more personal discourse. It’s much easier to make friends and have consistent interactions with people on Twitter than it is on Reddit, which is exactly what you need if you want to participate in a community. Case in point, I’ve made dozens of rust friends on Twitter, many of whom are in this room today, and I’m pretty sure I’ve made 0 friends on Reddit. So if you’re looking to make some rust friends and get more involved in the community, consider using Twitter and following every rustacean you can find.</p>
<h1 id="6-direct-encouragement">6. Direct Encouragement</h1>
<p>Now the last thing is something that hasn’t fully played itself out, but I expect it will be a major turning point as well. Giving talks! I’ve never given one other than at work in front of engineers I know personally, and I probably would have continued in that pattern for ages if it wasn’t for J Haigh. Manish had been trying to convince me to give a “contribution tour” talk at one of the meetups for a while, and I was interested but I didn’t feel ready yet, probably imposter syndrome or some nonsense like that. Then along comes J and they’re like, “Hey I see you’re active in the Rust OSS community, would you be interested in talking about any of it? COGoldRust currently has a cfp open”. Now, I was interested, terrified, but interested. I told them about Manish’s idea for a contribution tour and my apprehensions and feelings of being under qualified but they were nothing but encouraging, they helped helped me get over my feelings of imposter syndrome, made the cfp process seem much more approachable, and generally put a friendly face on the COGoldRust conference.</p>
<p>So I said yes, I’m interested, I’ll do my best, and I then promptly proceeded to procrastinate for the next two weeks. But that did not stop J! They came back and gently encouraged me to keep working on my submission more and helped me motivate when I probably wouldn’t have been able to alone. I expect I would have let the deadline pass and beat myself up over it if they hadn’t been so encouraging and helpful. I ended up throwing together a vague outline of the current talk, about how I learned Rust, what I contributed to, and what I learned in the process, which evolved into this current talk about how to effectively participate in the open source community.</p>
<p>I don’t really know what will end up happening as a result of being a speaker, but I’ll make sure to write a follow up blog in a few months to let yall know! Either way though J’s friendliness and encouragement is the main reason I’m here currently. If there are any take aways here it is to be like J and go out of your way to find qualified speakers and encourage them to take the leap. That could be the thing that helps them finally take the step they’ve always wanted to take.</p>
<h1 id="key-takeaways">Key Takeaways</h1>
<ul>
<li>CoC gives a sense of safety to underrepresented minorities and repels bad actors</li>
<li>This Week In Rust is an amazing tool for keeping informed on the goings on within the Rust community
<ul>
<li>TWIR is a great avenue for finding issues to work on or advertising issues you’d like help with</li>
<li>Navigating the issue boards by themselves was challenging, particularly hard to figure out which issues are available, which are interesting, and which you can learn the most from, organizing your issue list can reduce friction and attract more contributors</li>
</ul>
</li>
<li>Meetups and by extension conferences are one of the best ways to make friends in the Rust community and get/feel more connected</li>
<li>Mentors have a huge amplifying effect on how much work you can do and how approachable the work you want to do seems
<ul>
<li>Working on issues that you care about personally is the most rewarding</li>
<li>Don’t bite off more than you can chew. Max for me is extra open source 1 issue actively being worked on.</li>
<li>Keep track of your active work
<ul>
<li>Always open an issue when you have an idea of something that you want to work on or see done</li>
<li>Save a link to the issue somewhere, I’m using a random <a href="https://github.com/yaahc/prs/projects/1">github project</a> board for this.</li>
</ul>
</li>
</ul>
</li>
<li>Use twitter to make friends in the rust community.</li>
<li>Reach out to people, it makes a huge difference.</li>
</ul>
<h1 id="bonus-shameless-plug">Bonus: Shameless Plug</h1>
<p>So I want to take this opportunity to share a project that was inspired by the mentorship I’ve received from Manish, <a href="https://github.com/RustBeginners/awesome-rust-mentors">awesome-rust-mentors</a>. If you’re familiar with <code class="language-plaintext highlighter-rouge">awesome-rust</code> or <code class="language-plaintext highlighter-rouge">awesome-vim</code> or any other such currated awesome list you probably already know what this is. It’s a list of rustaceans who are interested in helping new contributors get involved and navigate the community, basically a bunch of Manishes. On it you’ll find people’s names, contact info, preferred pronouns, spoken languages, and topics of interest. If you’re interested in getting a mentor or being a mentor you should definitely go check it out.</p>
<p>I’m also planning on adding a “Call for Contributors” section to awesome-rust-mentors, which is specifically aimed at library specific mentorship for people who are looking to onboard new people in exchange for mentorship on their project. If you’re interested in that please let me know.</p>
<h1 id="thank-you-3">Thank You <3</h1>Navigating The Rust OSS Community2020 Rust: Accessibility2019-09-12T13:40:00+00:002019-09-12T13:40:00+00:00https://yaah.dev/2020-accessibility<p>For a while I didn’t really feel a need to write a blog post for 2020. As far
as I can tell we the Rust Community are doing a great job, and I just want us
to keep doing what we’ve been doing. My biggest worry is that the people who
contribute the most are dangerously close to burning out and I <a href="https://twitter.com/yaahc_/status/1189404270010884103?s=20">made a
tweet</a> much to that
effect.</p>
<p>But I was talking with someone recently about what makes Rust great, the
language and the community, and that ultimately boiled down to how inclusive
and accessible we are, and it made me think saying more than just “keep doing
what we’re doing and take care of yourselves” is in order.</p>
<p>To me, Rust is fundamentally a language of accessibility. I mean, the byline on
the main webpage for the lang is literally “A language empowering everyone to
build reliable and efficient software.” And if you look, you’ll notice that
everything we’ve done up to today shows a commitment to this idea.</p>
<ul>
<li>Cargo is all about making it easy to share and reuse code and setup projects.</li>
<li>The community was built around a strongly inclusive Code of Conduct and it
shows.</li>
<li>Async functions are all about making it easier to write futures and high
performance async io code.</li>
<li>Our compiler errors are famous for being incredibly helpful, frequently
linking directly to guides that help you understand the issue you’re running
into fully.</li>
<li>The compiler team views confusing compiler errors as a bug and it shows in
how helpful they are.
<ul>
<li>I mean this very seriously, just interact with Esteban once and you’ll
fall in love with opening issues for confusing compiler errors, he and
everyone else involved are SO NICE AND HELPFUL omg I love them.</li>
</ul>
</li>
<li>It’s a meme that our community is run by queers because of how inclusive we
are of lgbt people.</li>
</ul>
<p>Every point here has one thing in common, and that’s accessibility. I think the
most important thing going forward is to keep focused on what made us great in
the first place. Shiney new tech is great but it’s even better when everyone
gets to use it.</p>
<p>Now, I don’t really have any examples of people going against accessibility,
thankfully, but that doesn’t mean we still don’t have room to improve! To get
you started here are some ideas I was able to come up with on what we can all
do to keep working on accessibility.</p>
<ul>
<li>Write tutorials and docs that help people use your projects/tools/libraries
so that more people can jump in and use / contribute.
<ul>
<li>I recommend watching <a href="https://llvmdevmtg2019.sched.com/event/W2zN/llvm-tutorials-how-to-write-beginner-friendly-inclusive-tutorials">this wonderful talk by Meike Baumgärtner when the video comes out</a>.</li>
<li>[From <a href="https://twitter.com/imperioworld_">@imperioworld_</a>] a very nice thing to have is always providing as much information as possible on how to use your crate. That goes through writing documentation for every items with code examples (I insist on this one)</li>
</ul>
</li>
<li>Write blog posts so people know what you’re working on and can get excited
about it.</li>
<li>Focus not just on adding new features, but on making those features easy to
use.</li>
<li>Work on accessibility for our webpages and localization so other natural
language speakers can get involved at the same level as english speakers.
<ul>
<li>The replies to <a href="https://twitter.com/yaahc_/status/1193973822635905024?s=20">this tweet</a> have helpful suggestions on how to make your webpages accessible.</li>
</ul>
</li>
<li>Think about mentoring people or getting a mentor.
<ul>
<li>Insert Shameless Plug of <a href="https://rustbeginners.github.io/awesome-rust-mentors/">awesome-rust-mentors</a></li>
</ul>
</li>
<li>And many more ideas that I’m sure other wonderful Rustaeceans will come up with.
<ul>
<li>Seriously though, if you have any more ideas on accessibility please @ me
on twitter and tell me, I’ll add your ideas to this list.</li>
</ul>
</li>
<li>[From <a href="https://twitter.com/Sunjay03">@Sunjay03</a>] Add a code of conduct to your project, <a href="https://help.github.com/en/github/building-a-strong-community/adding-a-code-of-conduct-to-your-project">heres why.</a></li>
<li>[From <a href="https://twitter.com/Sunjay03">@Sunjay03</a>] Contribute to other projects to help them be more
accessible, rather than putting all the burden on the maintainer.</li>
<li>[From <a href="https://twitter.com/timClicks">@timClicks</a>] Prefer shorter sentences in blog posts about Rust. Many (majority?) of the community use English as a second language. Shorter sentences are much easier to translate.</li>
<li>[From <a href="https://twitter.com/TheWholeDavid">@TheWholeDavid</a>] Engage with the dyslexic community on new language features and syntax, library API names, etc. Dyslexic people, for the most part, make the same mistakes when reading things that everyone does at least occasionally, but the difference is we make them almost every time.</li>
</ul>
<p>I want nothing more than to see rust continue to grow and continue to be the
amazing lang/community it is today, and I think the key to this is a continuing
focus on accessibilty. Be kind, be helpful, stay accessibilty.</p>
<p><strong>PS</strong>: <em>And of course postfix macros are CRITICAL, someone please, my CLOC is dying.</em></p>For a while I didn’t really feel a need to write a blog post for 2020. As far as I can tell we the Rust Community are doing a great job, and I just want us to keep doing what we’ve been doing. My biggest worry is that the people who contribute the most are dangerously close to burning out and I made a tweet much to that effect.Scoped, Structured, and Distributed Logging2019-07-23T10:23:00+00:002019-07-23T10:23:00+00:00https://yaah.dev/distributed-structured-tracing<h1 id="scoped-structured-and-distributed-logging---jane-lusby">Scoped, Structured, and Distributed Logging - Jane Lusby</h1>
<p>Okay, so what is this talk? This talk is intended to introduce my coworkers to
logging concepts I learned about at a rust meetup. Eliza Wiesman gave a talk on
her library <code class="language-plaintext highlighter-rouge">tokio-rs/tracing</code> https://www.youtube.com/watch?v=j_kXRg3zlec
which I found compelling, in no small part because I felt it would be useful to
have similar features in our clusters.</p>
<p><code class="language-plaintext highlighter-rouge">tracing</code> itself is a library that is based on Distributed Tracing as defined by the microservices world.</p>
<p>The main concepts I would like to introduce are the Event+Span model of
logging, structured logging, and as little rust syntax as is necessary to
demonstrait those two ideas. The Event+Span model of logging encodes
relationships between log messsages and units of work done by the system and
makes it easier to find logs which are tied logically to some event on another
node, and to isolate logs from those on different threads to handle the
confusion and chattiness that is common when debugging heavily asyncronous
code. Structured logging is a paradigm for how data is formatted so that it can
be output in various formats (plain text vs json vs binary) and parsed more
effectively.</p>
<p>All of the examples will be in rust, mostly because none of the c++
implementations of distributed tracing or structured logging are anywhere near
as clean to look at as <code class="language-plaintext highlighter-rouge">tracing</code> is. This is 100% thanks to rust’s powerful
macro system.</p>
<h2 id="definitions">Definitions</h2>
<p>Some quick definitions so the examples make sense</p>
<ul>
<li>Event - Essentially a log message</li>
<li>Span - An abstract representation of a unit of work, usually a few ids, a
name, and whatever associated data you want.</li>
<li>Trace - A Span and all of its children</li>
</ul>
<p>The secret sauce is in that children bit. Every span and event has a parent
span associated with it. These relationships define a treelike structure that
represents the various code paths and events you go through when completing a
unit of work.</p>
<h2 id="a-basic-example-logging-in-rust">A Basic Example, logging in rust.</h2>
<p>The following example is taken from the <code class="language-plaintext highlighter-rouge">log</code> crate, not <code class="language-plaintext highlighter-rouge">tracing</code>. It is the
current existing standard for logging in the rust ecosystem, you can view
<code class="language-plaintext highlighter-rouge">tracing</code> as an extension upon <code class="language-plaintext highlighter-rouge">log</code>’s standard. The includes are from
<code class="language-plaintext highlighter-rouge">tracing</code> rather than <code class="language-plaintext highlighter-rouge">log</code> but they’re a drop in replacement, you can replace
<code class="language-plaintext highlighter-rouge">tracing</code> with <code class="language-plaintext highlighter-rouge">log</code> on line 2 and everything will work the same more or less.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c">// Import `tracing`'s macros rather than `log`'s</span>
<span class="k">use</span> <span class="nn">tracing</span><span class="p">::{</span><span class="n">span</span><span class="p">,</span> <span class="n">info</span><span class="p">,</span> <span class="n">warn</span><span class="p">,</span> <span class="n">Level</span><span class="p">};</span>
<span class="c">// unchanged from here forward</span>
<span class="k">pub</span> <span class="k">fn</span> <span class="nf">shave_the_yak</span><span class="p">(</span><span class="n">yak</span><span class="p">:</span> <span class="o">&</span><span class="k">mut</span> <span class="n">Yak</span><span class="p">)</span> <span class="p">{</span>
<span class="nd">info!</span><span class="p">(</span><span class="n">target</span><span class="p">:</span> <span class="s">"yak_events"</span><span class="p">,</span> <span class="s">"Commencing yak shaving for {:?}"</span><span class="p">,</span> <span class="n">yak</span><span class="p">);</span>
<span class="k">loop</span> <span class="p">{</span>
<span class="k">match</span> <span class="nf">find_a_razor</span><span class="p">()</span> <span class="p">{</span>
<span class="nf">Ok</span><span class="p">(</span><span class="n">razor</span><span class="p">)</span> <span class="k">=></span> <span class="p">{</span>
<span class="nd">info!</span><span class="p">(</span><span class="s">"Razor located: {}"</span><span class="p">,</span> <span class="n">razor</span><span class="p">);</span>
<span class="n">yak</span><span class="nf">.shave</span><span class="p">(</span><span class="n">razor</span><span class="p">);</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="nf">Err</span><span class="p">(</span><span class="n">err</span><span class="p">)</span> <span class="k">=></span> <span class="p">{</span>
<span class="nd">warn!</span><span class="p">(</span><span class="s">"Unable to locate a razor: {}, retrying"</span><span class="p">,</span> <span class="n">err</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c">// Dummy impls to make the example compile</span>
<span class="nd">#[derive(Debug)]</span> <span class="k">pub</span> <span class="k">struct</span> <span class="nf">Yak</span><span class="p">(</span><span class="nb">String</span><span class="p">);</span>
<span class="k">impl</span> <span class="n">Yak</span> <span class="p">{</span> <span class="k">fn</span> <span class="nf">shave</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="mi">_</span><span class="p">:</span> <span class="nb">u32</span><span class="p">)</span> <span class="p">{}</span> <span class="p">}</span>
<span class="k">fn</span> <span class="nf">find_a_razor</span><span class="p">()</span> <span class="k">-></span> <span class="n">Result</span><span class="o"><</span><span class="nb">u32</span><span class="p">,</span> <span class="nb">u32</span><span class="o">></span> <span class="p">{</span> <span class="nf">Ok</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">}</span>
</code></pre></div></div>
<p>So in this example we have a function, on entry we log a message saying we are
gonna shave a yak, and what yak we’re going to shave, then we loop and find a
razor, and if we get one we shave the yak and log that, and if we dont we warn
that we cant find a razor.</p>
<h2 id="the-same-basic-example-reloaded">The Same Basic Example: Reloaded</h2>
<p>So now we will rewrite this example to use features from tracing. This will
introduce spans and structured log messages, and will lead into the rest of the
talk.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">use</span> <span class="nn">tracing</span><span class="p">::{</span><span class="n">span</span><span class="p">,</span> <span class="n">info</span><span class="p">,</span> <span class="n">warn</span><span class="p">,</span> <span class="n">Level</span><span class="p">};</span>
<span class="k">pub</span> <span class="k">fn</span> <span class="nf">shave_the_yak</span><span class="p">(</span><span class="n">yak</span><span class="p">:</span> <span class="o">&</span><span class="k">mut</span> <span class="n">Yak</span><span class="p">)</span> <span class="p">{</span>
<span class="c">// create and enter a span to represent the scope</span>
<span class="k">let</span> <span class="n">span</span> <span class="o">=</span> <span class="nd">span!</span><span class="p">(</span><span class="nn">Level</span><span class="p">::</span><span class="n">TRACE</span><span class="p">,</span> <span class="s">"shave_the_yak"</span><span class="p">,</span> <span class="o">?</span><span class="n">yak</span><span class="p">);</span>
<span class="k">let</span> <span class="mi">_</span><span class="n">enter</span> <span class="o">=</span> <span class="n">span</span><span class="nf">.enter</span><span class="p">();</span>
<span class="c">// Since the span is annotated with the yak, it is part of the context</span>
<span class="c">// for everything happening inside the span. Therefore, we don't need</span>
<span class="c">// to add it to the message for this event, as the `log` crate does.</span>
<span class="nd">info!</span><span class="p">(</span><span class="n">target</span><span class="p">:</span> <span class="s">"yak_events"</span><span class="p">,</span> <span class="s">"Commencing yak shaving"</span><span class="p">);</span>
<span class="k">loop</span> <span class="p">{</span>
<span class="k">match</span> <span class="nf">find_a_razor</span><span class="p">()</span> <span class="p">{</span>
<span class="nf">Ok</span><span class="p">(</span><span class="n">razor</span><span class="p">)</span> <span class="k">=></span> <span class="p">{</span>
<span class="c">// We can add the razor as a field rather than formatting it</span>
<span class="c">// as part of the message, allowing subscribers to consume it</span>
<span class="c">// in a more structured manner:</span>
<span class="nd">info!</span><span class="p">({</span> <span class="o">%</span><span class="n">razor</span> <span class="p">},</span> <span class="s">"Razor located"</span><span class="p">);</span>
<span class="n">yak</span><span class="nf">.shave</span><span class="p">(</span><span class="n">razor</span><span class="p">);</span>
<span class="k">break</span><span class="p">;</span>
<span class="p">}</span>
<span class="nf">Err</span><span class="p">(</span><span class="n">err</span><span class="p">)</span> <span class="k">=></span> <span class="p">{</span>
<span class="c">// However, we can also create events with formatted messages,</span>
<span class="c">// just as we would for log records.</span>
<span class="nd">warn!</span><span class="p">(</span><span class="s">"Unable to locate a razor: {}, retrying"</span><span class="p">,</span> <span class="n">err</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nd">#[derive(Debug)]</span> <span class="k">pub</span> <span class="k">struct</span> <span class="nf">Yak</span><span class="p">(</span><span class="nb">String</span><span class="p">);</span>
<span class="k">impl</span> <span class="n">Yak</span> <span class="p">{</span> <span class="k">fn</span> <span class="nf">shave</span><span class="p">(</span><span class="o">&</span><span class="k">mut</span> <span class="k">self</span><span class="p">,</span> <span class="mi">_</span><span class="p">:</span> <span class="nb">u32</span><span class="p">)</span> <span class="p">{}</span> <span class="p">}</span>
<span class="k">fn</span> <span class="nf">find_a_razor</span><span class="p">()</span> <span class="k">-></span> <span class="n">Result</span><span class="o"><</span><span class="nb">u32</span><span class="p">,</span> <span class="nb">u32</span><span class="o">></span> <span class="p">{</span> <span class="nf">Ok</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="p">}</span>
</code></pre></div></div>
<p>Ok so what changed, now instead of logging a message on entering the function
we create a span and do something with our yak argument and then enter said
span. Then we log the same message as before to say that we’re starting our yak
shave. Then in the loop when we have a razor instead of formatting the razor
into the log string we do something weird with it. The warn log line is
unchanged.</p>
<p>Okay so you probably guessed already but those weird things are structured
logging. The <code class="language-plaintext highlighter-rouge">%</code> and <code class="language-plaintext highlighter-rouge">?</code> infront of them are a special syntax implemented by
the macros in <code class="language-plaintext highlighter-rouge">tracing</code> that tell the logger whether or not to use <code class="language-plaintext highlighter-rouge">Debug</code> or
<code class="language-plaintext highlighter-rouge">Display</code> when formatting the data. These are just two traits for how to
essentially ostream objects in rust. Display is the default user defined
version, and is intended to be user facing. <code class="language-plaintext highlighter-rouge">Debug</code> is generally automatically
generated by the compiler for you and is essentially a jsonification of the
struct for use by developers when debugging. In this case <code class="language-plaintext highlighter-rouge">%</code> means format this
with <code class="language-plaintext highlighter-rouge">Display</code> and <code class="language-plaintext highlighter-rouge">?</code> means format this with <code class="language-plaintext highlighter-rouge">Debug</code>.</p>
<h2 id="what-does-the-output-look-like">What does the output look like?</h2>
<p>This is a tricky question because it’s not actually defined by <code class="language-plaintext highlighter-rouge">tracing</code> but by
the subscriber implementation, aka the thing that tracing passes all the events
to and delegates all decision logic to. The tracing group provides a couple of
implementations of subscribers, we will focus on <code class="language-plaintext highlighter-rouge">tracing-fmt</code> because its the
most basic “normal” logger that converts your events straight into log lines on
stdout, just like you’d expect.</p>
<p>The basic example above might print something like this.</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>Jul 23 14:01:51.803 INFO shave_the_yak{yak=Yak{name: "hairy"}}: Commencing yak shaving
Jul 23 14:01:51.804 INFO shave_the_yak{yak=Yak{name: "hairy"}}: Razor located razor=1
</code></pre></div></div>
<p>As you can see each log line is associated with the parent span, this logger
outputs all parent spans and the associated data of each parent span and then
the event message followed by any associated data of the log event. The actual
output can be whatever you like, the point is that when you go to log you have
all of this data available to you in a highly structured manner.</p>
<p>The structured logging lets the logger format and stringify the name of your
fields and append them or prepend them nicely and consistently. No more having
to write <code class="language-plaintext highlighter-rouge">"Shaving yak (" << yak << ")"</code> or similar custom and inconsistent
formatting for each and every log message.</p>
<h2 id="what-am-i-proposing-why-is-this-useful-for-scale-computing">What am I proposing? Why is this useful for Scale Computing?</h2>
<p>What im not proposing is replacing or rewriting our entire log system. I want
to see what incremental changes we can make to our existing log framework to
move us towards a Span+Event model of logging.</p>
<p>With our tasks especially, its very common to have a few fields that we put in
every log message like the rsdID or journalID or w/e, that really should be
associated with the span that represents the task.</p>
<p>Many of our tasks spin up tasks on remote nodes and it can be hard to trace a
parent child relationship between them. We do have parent ids for tasks, which
is amazing and is already I think part of the way there, but its not really
formalized or easy to work with, and its very specific to our Task/PDU system.</p>
<p>With general logging im constantly having to write messages like <code class="language-plaintext highlighter-rouge">"variable
name = " << variable"</code> just to get some basic debug information available. I
want structured logging so I can just write something like <code class="language-plaintext highlighter-rouge">DEBUG("whatever
event just happened", var)</code> and have it magically and consistently output the
variable name and its value, and possibly output the data as json so I can
query for a specific set of lines and then grab the associated json data and
parse / process it.</p>
<p>The goal here is to make logs easier to write, easier to process, and easier to
search. I doubt we’ll ever get all the way there to every feature I can
imagine, but I think there are certainly some low hanging fruit that we could
pick, and I want to make sure everyone is familiar with these concepts so when
opportunities arrise we’re ready for them.</p>Scoped, Structured, and Distributed Logging - Jane LusbyMy Preference For Await Syntax2019-05-02T14:24:00+00:002019-05-02T14:24:00+00:00https://yaah.dev/await<h1 id="await-is-best-imo"><code class="language-plaintext highlighter-rouge">await!()</code> is best IMO</h1>
<p>You might be wondering why anyone should care what I think about the await syntax or why my opinion matters. You shouldn’t, It doesn’t. Particularly because my syntax choice has nothing to do with await / async. But if you’re still curious keep reading!</p>
<h2 id="why-await">Why <code class="language-plaintext highlighter-rouge">await!()</code>?</h2>
<p>Simply because I want associated macros. Why do I want associated macros? Because I want to be able to have formatted prints in <code class="language-plaintext highlighter-rouge">expect</code> calls. The rust I dream of would allow this.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">toml</span><span class="p">::</span><span class="nf">from_str</span><span class="p">(</span><span class="o">&</span><span class="n">file_contents</span><span class="p">)</span><span class="py">.expect</span><span class="o">!</span><span class="p">(</span><span class="s">"{:?} contained valid toml data"</span><span class="p">,</span> <span class="n">path</span><span class="p">);</span>
</code></pre></div></div>
<p>Instead of this</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nn">toml</span><span class="p">::</span><span class="nf">from_str</span><span class="p">(</span><span class="o">&</span><span class="n">file_contents</span><span class="p">)</span><span class="nf">.unwrap_or_else</span><span class="p">(|</span><span class="n">e</span><span class="p">|</span> <span class="p">{</span>
<span class="nd">panic!</span><span class="p">(</span>
<span class="s">"expected that {:?} contained valid toml, error: {:?}"</span><span class="p">,</span>
<span class="n">path</span><span class="p">,</span> <span class="n">e</span>
<span class="p">)</span>
<span class="p">})</span>
</code></pre></div></div>
<p>Right now you have to inline a <code class="language-plaintext highlighter-rouge">format!</code> call, which then makes clippy yell at you because it will always get invoked, so now you have to replace <code class="language-plaintext highlighter-rouge">expect</code> with an <code class="language-plaintext highlighter-rouge">unwrap_or_else</code> and if you still want to follow the <code class="language-plaintext highlighter-rouge">expect</code> pattern where you write down what invariant you expected was true that made it legal you no longer have that context, and if you’re OCD and want the message to look like the ones that <code class="language-plaintext highlighter-rouge">expect</code> formats for you, you have to go look up what exactly that is and include that too!</p>
<p>It bothers me! But I figure if we had associated macros we could easily add <code class="language-plaintext highlighter-rouge">expect!</code> to <code class="language-plaintext highlighter-rouge">Result</code> and <code class="language-plaintext highlighter-rouge">Option</code> and massage away one of my most insignificant annoyances. And I figure if <code class="language-plaintext highlighter-rouge">await!()</code> got stabilized more people would think about and ask for associated macros in general.</p>
<h2 id="conclusions">Conclusions</h2>
<p>So yea, that’s why I want <code class="language-plaintext highlighter-rouge">.await!()</code> to be stabilized.</p>await!() is best IMOLifetime definitions2019-04-22T00:00:00+00:002019-04-22T00:00:00+00:00https://yaah.dev/lifetimes<h1 id="disclaimer">DISCLAIMER</h1>
<p>This is a WIP. It does not represent a full or even necessarily correct
explanation of what these terms are or mean or how they relate. This is my
attempt at articulating how I’ve taken to understanding them. This is going to
evolve over time as I understand lifetimes and borrows better and I hope to
rewrite this page as my understanding improves.</p>
<h1 id="confusing-terms">Confusing terms</h1>
<p>My problem with understanding lifetime heavy code is that I find all the terms
involved confusable. This post is my attempt to record clear distinctions
between the terms. Theoretically, these definitions will help ground me when
I’m reasoning about lifetimes.</p>
<h1 id="lifetime">Lifetime</h1>
<p>A lifetime is a region or scope for which a concrete value is valid. All
variables have a lifetime implicitly, or in the case of <code class="language-plaintext highlighter-rouge">static</code>, explicitly.
These lifetimes are concrete. They are not the annotations you see in code like
<code class="language-plaintext highlighter-rouge">fn foo<'a>(input: &'a i32) -> &'a i32</code>, those are lifetime variables.</p>
<h2 id="lifetime-variable">Lifetime variable</h2>
<p>A type-level variable that refers to a specific scope or region of code (lifetime).</p>
<h1 id="reference">Reference</h1>
<p>A reference is a restricted pointer to a concrete value. You can dereference
them to access the value they point to. The compiler assumes they are never
null.</p>
<h1 id="borrow">Borrow</h1>
<p>A borrow is a reference bound by a concrete lifetime. A reference A is a borrow
of a value V if the lifetime variable constraining reference A refers to the
literal lifetime of the value V.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">let</span> <span class="n">x</span><span class="p">:</span> <span class="nb">i32</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">let</span> <span class="n">y</span><span class="p">:</span> <span class="o">&</span><span class="nv">'a</span> <span class="nb">i32</span> <span class="o">=</span> <span class="o">&</span><span class="n">x</span><span class="p">;</span>
</code></pre></div></div>
<p>In the above example, the lifetime variable ‘a refers to the literal lifetime
of the variable x. y contains a borrow of x. The above code isn’t actually
valid though because you cannot declare lifetimes in <code class="language-plaintext highlighter-rouge">let statements</code>.</p>
<p>The distinctions between borrows and references are not common as far as I can
tell. This is possibly because references cannot exist without being borrows. I
think of references as the generic version of a borrow, or possibly as a
superset of borrows.</p>
<h2 id="rules-and-features-of-borrows">Rules and Features of borrows</h2>
<ul>
<li>borrows cannot outlive the lifetime they’re templated on, aka, the concrete
lifetime the borrow was bound to. In the above example, the borrow y cannot
outlive the value x.
<ul>
<li>The above defines the relationship between a borrows reference and its
lifetime variable. A borrow’s reference is valid for the scope or range
(lifetime) refered to by the borrows lifetime variable.</li>
</ul>
</li>
<li>Borrows are implicitly cast to other borrows so long as they fulfill the
above relationship for the target borrow type.</li>
</ul>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">static</span> <span class="n">X</span><span class="p">:</span> <span class="nb">i32</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="k">fn</span> <span class="n">foo</span><span class="o"><</span><span class="nv">'a</span><span class="o">></span><span class="p">(</span><span class="n">input</span><span class="p">:</span> <span class="o">&</span><span class="nv">'a</span> <span class="nb">i32</span><span class="p">)</span> <span class="k">-></span> <span class="o">&</span><span class="nv">'a</span> <span class="nb">i32</span> <span class="p">{</span>
<span class="o">&</span><span class="n">X</span>
<span class="p">}</span>
</code></pre></div></div>
<p>The above code is valid because the lifetime of the borrow of X <code class="language-plaintext highlighter-rouge">&'static i32</code>
is valid for the entire lifetime of any generic lifetime <code class="language-plaintext highlighter-rouge">'a</code>. Note, the
compiler sees the output borrow an instance of the input’s borrow-type.
Whatever concrete variable this lifetime is infered from will still be borrowed
as long as the borrow returned by <code class="language-plaintext highlighter-rouge">foo</code> is in scope. That means the following
program wont compile.</p>
<div class="language-rust highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">pub</span> <span class="k">fn</span> <span class="nf">main</span><span class="p">()</span> <span class="p">{</span>
<span class="k">let</span> <span class="n">v</span><span class="p">:</span> <span class="o">&</span><span class="nb">i32</span><span class="p">;</span>
<span class="p">{</span>
<span class="k">let</span> <span class="n">y</span><span class="p">:</span> <span class="nb">i32</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span>
<span class="n">v</span> <span class="o">=</span> <span class="nf">foo</span><span class="p">(</span><span class="o">&</span><span class="n">y</span><span class="p">);</span>
<span class="p">}</span>
<span class="nd">println!</span><span class="p">(</span><span class="s">"{}"</span><span class="p">,</span> <span class="n">v</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>This wont compile because the compiler thinks <code class="language-plaintext highlighter-rouge">y</code> goes out of scope while it’s
still borrowed.</p>DISCLAIMER