<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Eduardo Pinho</title>
    <description>The latest articles on DEV Community by Eduardo Pinho (@e_net4).</description>
    <link>https://dev.to/e_net4</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F224840%2Fc441e62f-86c2-4619-9721-2223d09d75c3.jpeg</url>
      <title>DEV Community: Eduardo Pinho</title>
      <link>https://dev.to/e_net4</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/e_net4"/>
    <language>en</language>
    <item>
      <title>A new milestone: what's new in DICOM-rs 0.6.0</title>
      <dc:creator>Eduardo Pinho</dc:creator>
      <pubDate>Sun, 23 Jul 2023 15:55:39 +0000</pubDate>
      <link>https://dev.to/e_net4/a-new-milestone-whats-new-in-dicom-rs-060-28md</link>
      <guid>https://dev.to/e_net4/a-new-milestone-whats-new-in-dicom-rs-060-28md</guid>
      <description>&lt;p&gt;I recently &lt;a href="https://github.com/Enet4/dicom-rs/discussions/398" rel="noopener noreferrer"&gt;released DICOM-rs v0.6.0&lt;/a&gt;. DICOM-rs is an implementation of the &lt;a href="https://dicomstandard.org" rel="noopener noreferrer"&gt;DICOM standard&lt;/a&gt; for the next generation of medical imaging systems. Comprising a total of 71 pull requests, there is so much in this version that I felt the need to make a blog post to further expand on its major changes and talk about some cool new features.&lt;/p&gt;

&lt;h2&gt;
  
  
  So what's new?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Attribute selectors
&lt;/h3&gt;

&lt;p&gt;It was not a secret that accessing specific elements from a DICOM object could get tricky, especially when working with nested data sets. In fact, accessing nested data set elements is not part of an obscure use case. This happens to be a common necessity, from working with various kinds of multi-frame instances, to structured reports.&lt;/p&gt;

&lt;p&gt;For instance, this code snippet &lt;a href="https://github.com/Enet4/dicom-rs/discussions/334" rel="noopener noreferrer"&gt;brought up to discussion&lt;/a&gt; tries to retrieve the &lt;em&gt;Referenced SOP Instance UID&lt;/em&gt; from a &lt;em&gt;Referenced Image Sequence&lt;/em&gt;, as part of the &lt;em&gt;Shared Functional Groups Sequence&lt;/em&gt;. This means navigating two levels of nested sequences.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;open_file&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"../dicoms/my_dicom_path.dcm"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;referenced_sop_instance_uid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;
    &lt;span class="nf"&gt;.element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SHARED_FUNCTIONAL_GROUPS_SEQUENCE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
    &lt;span class="nf"&gt;.items&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;REFERENCED_IMAGE_SEQUENCE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
    &lt;span class="nf"&gt;.items&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.unwrap&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;REFERENCED_SOP_INSTANCE_UID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
    &lt;span class="nf"&gt;.to_str&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Handling each error case possible when accessing deeply nested attributes becomes unwieldy and prone to mistakes, as evidenced by this example. In this case, an error is correctly raised when either of the three data elements requested do not exist, or when the &lt;em&gt;Referenced SOP Instance UID&lt;/em&gt; value could not be converted to a string (e.g. if it turns out to be a sequence itself). However, &lt;em&gt;this code panics&lt;/em&gt; if either of the two data set sequences do not contain any items, or if the data happens to be malformed and the sequence element does not really have a sequence. These situations might seem unlikely in a controlled scenario, but it opens room for attackers to feed meticulously crafted inputs which take down the whole thread or process!&lt;/p&gt;

&lt;p&gt;The main difficulty comes from the fact that &lt;code&gt;items()&lt;/code&gt; and &lt;code&gt;first()&lt;/code&gt; return an &lt;code&gt;Option&lt;/code&gt;, which can only be raised with the &lt;code&gt;?&lt;/code&gt; operator in a function context which also returns an &lt;code&gt;Option&lt;/code&gt;. Since these methods are chained in methods returning a &lt;code&gt;Result&lt;/code&gt;, these options need to be transformed to a &lt;code&gt;Result&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With &lt;a href="https://github.com/shepmaster/snafu" rel="noopener noreferrer"&gt;Snafu&lt;/a&gt;, this can be done by adding context methods to the chain, either with a new custom error type or with the dynamic &lt;code&gt;Whatever&lt;/code&gt; error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;referenced_sop_instance_uid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;
    &lt;span class="nf"&gt;.element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SHARED_FUNCTIONAL_GROUPS_SEQUENCE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.whatever_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Missing Shared Functional Groups Sequence"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
    &lt;span class="nf"&gt;.items&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.whatever_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Shared Functional Groups is not a sequence"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
    &lt;span class="nf"&gt;.first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.whatever_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No items in Shared Functional Groups Sequence"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
    &lt;span class="nf"&gt;.element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;REFERENCED_IMAGE_SEQUENCE&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.whatever_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Missing Referenced Images Sequence"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
    &lt;span class="nf"&gt;.items&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.whatever_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Referenced Images Sequence is not a sequence"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
    &lt;span class="nf"&gt;.first&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.whatever_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"No items in Referenced Images Sequence"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
    &lt;span class="nf"&gt;.element&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;REFERENCED_SOP_INSTANCE_UID&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.whatever_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Missing Referenced SOP Instance UID"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
    &lt;span class="nf"&gt;.to_str&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.whatever_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Referenced SOP Instance UID cannot be converted to a string"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Needless to say, even the more correct version of the snippet continues to be very verbose.&lt;/p&gt;

&lt;p&gt;The attribute selector API has come to change this, while also bringing new ways to edit and build DICOM objects. A new key component in the &lt;code&gt;dicom-core&lt;/code&gt; crate is the &lt;code&gt;AttributeSelector&lt;/code&gt;, which uniquely describes a specific attribute in a DICOM object, even if they are nested, by composing a list of sequence items and attribute tags. Attribute selectors can be constructed from tuples of tags, or tuples of interleaved tags and item indices. The following two selectors are equivalent:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;dicom_core&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;ops&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;AttributeSelector&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AttributeSelector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nn"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SHARED_FUNCTIONAL_GROUPS_SEQUENCE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nn"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;REFERENCED_IMAGE_SEQUENCE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nn"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;REFERENCED_SOP_INSTANCE_UID&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// or omit the item indices to assume the first item&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;AttributeSelector&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nn"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SHARED_FUNCTIONAL_GROUPS_SEQUENCE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nn"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;REFERENCED_IMAGE_SEQUENCE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nn"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;REFERENCED_SOP_INSTANCE_UID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the example above can now be written like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;referenced_sop_instance_uid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;
    &lt;span class="nf"&gt;.value_at&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;
        &lt;span class="nn"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SHARED_FUNCTIONAL_GROUPS_SEQUENCE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nn"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;REFERENCED_IMAGE_SEQUENCE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nn"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;REFERENCED_SOP_INSTANCE_UID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="nf"&gt;.whatever_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Could not retrieve Referenced SOP Instance UID"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;
    &lt;span class="nf"&gt;.to_str&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.whatever_context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Referenced SOP Instance UID cannot be converted to a string"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Plus, there is a text syntax for turning strings into attribute selectors, so that tools may benefit from user-input attribute selectors. &lt;code&gt;dicom-findscu&lt;/code&gt; already uses it for building the C-FIND query object based on user input.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# retrieve the modality worklist information&lt;/span&gt;
&lt;span class="c"&gt;# for scheduled procedures where the patient has arrived&lt;/span&gt;
dicom-findscu INFO@pacs.example.com:1045 &lt;span class="nt"&gt;--mwl&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-q&lt;/span&gt; ScheduledProcedureStepSequence &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;-q&lt;/span&gt; ScheduledProcedureStepSequence.ScheduledProcedureStepStatus&lt;span class="o"&gt;=&lt;/span&gt;ARRIVED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Improved object and value ergonomics
&lt;/h3&gt;

&lt;p&gt;Construction of objects from scratch also had a few unnecessary pain-points sometimes. Here is &lt;a href="https://github.com/Enet4/dicom-rs/discussions/351" rel="noopener noreferrer"&gt;another example from discussions&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;start_date_sequence&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DataElement&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;InMemDicomObject&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;InMemFragment&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;DataElement&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nn"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SCHEDULED_PROCEDURE_STEP_START_DATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nn"&gt;VR&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;LO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nn"&gt;PrimitiveValue&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"19951015"&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;scheduled_procedure_step_sequence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;DataElement&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nn"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SCHEDULED_PROCEDURE_STEP_SEQUENCE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nn"&gt;VR&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SQ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nn"&gt;Value&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new_sequence&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;smallvec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;start_date_sequence&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nn"&gt;Length&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;UNDEFINED&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scheduled_procedure_step_sequence&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This attempt failed with a compile time error due to the inflexible type parameters of &lt;code&gt;Value::new_sequence&lt;/code&gt;. In general, building up a DICOM value was a bit troublesome when dealing with data set sequences or pixel data fragment sequences. With the new version, the base DICOM value type &lt;code&gt;DicomValue&lt;/code&gt; was further decomposed so that data set sequences now have their own type &lt;code&gt;DataSetSequence&lt;/code&gt;, which not only has nicer conversions from sequences of items, it can also be passed instead of &lt;code&gt;DicomValue&lt;/code&gt; directly to &lt;code&gt;DataElement::new&lt;/code&gt;. In addition, string types &lt;code&gt;&amp;amp;str&lt;/code&gt; and &lt;code&gt;String&lt;/code&gt; were granted a privileged conversion path to &lt;code&gt;DicomValue&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;scheduled_procedure_step_sequence&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;DataElement&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nn"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SCHEDULED_PROCEDURE_STEP_SEQUENCE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nn"&gt;VR&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SQ&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nn"&gt;DataSetSequence&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;vec!&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="nn"&gt;DataElement&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nn"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SCHEDULED_PROCEDURE_STEP_START_DATE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="nn"&gt;VR&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;LO&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s"&gt;"19951015"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;]),&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scheduled_procedure_step_sequence&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are a few other breaking changes which were made either to fix existing problems, reduce building costs for unused features, or increase convenience of use. A few examples follow.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The type parameter &lt;code&gt;P&lt;/code&gt; in &lt;code&gt;DataElement&amp;lt;I, P&amp;gt;&lt;/code&gt; and &lt;code&gt;DicomValue&amp;lt;I, P&amp;gt;&lt;/code&gt; now defaults to the current definition for an in-memory pixel data fragment, which is an alias to &lt;code&gt;Vec&amp;lt;u8&amp;gt;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;It is no longer possible to compare a &lt;code&gt;Tag&lt;/code&gt; for equality against a &lt;code&gt;[u16; 2]&lt;/code&gt; or a &lt;code&gt;(u16, u16)&lt;/code&gt;. This was a bit of an obscure capability which was bringing issues when trying to use &lt;code&gt;assert_eq!&lt;/code&gt; in places where inference to &lt;code&gt;Tag&lt;/code&gt; was expected, and developers are expected to convert those to &lt;code&gt;Tag&lt;/code&gt; first anyway.&lt;/li&gt;
&lt;li&gt;Some type name changes were made in &lt;code&gt;dicom-dictionary-std&lt;/code&gt; to accommodate other kinds of dictionaries. &lt;/li&gt;
&lt;li&gt;Error types coming from operations in &lt;code&gt;dicom-ul&lt;/code&gt; and &lt;code&gt;dicom-object&lt;/code&gt; have been redesigned to be smaller in size and more focused on the operation at hand. Any code which mentioned the specific error type by name may need to be updated accordingly.&lt;/li&gt;
&lt;li&gt;A bug was fixed in &lt;code&gt;AbortRQServiceProviderReason&lt;/code&gt;, which had two different errors combined into the same variant by mistake.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;dicom-pixeldata&lt;/code&gt; now puts conversion of DICOM objects into dynamic image values behind the "image" feature, and to a multi-dimensional arrays behind the "ndarray" feature. The "rayon" feature can also be excluded for use in an environment which does not support &lt;a href="https://github.com/rayon-rs/rayon" rel="noopener noreferrer"&gt;Rayon&lt;/a&gt; right off the bat.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Operations API
&lt;/h3&gt;

&lt;p&gt;Still speaking of ergonomics, creating new objects is even easier with the &lt;em&gt;attribute operations API&lt;/em&gt;! &lt;code&gt;dicom-core&lt;/code&gt; now comes with &lt;code&gt;ApplyOp&lt;/code&gt; trait, a common API for manipulating attributes in several ways. Thanks to attribute actions such as &lt;code&gt;Set&lt;/code&gt;, we can build the same example above with less lines and less chances of making mistakes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// create an empty sequence&lt;/span&gt;
&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="nf"&gt;.apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;AttributeOp&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nn"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SCHEDULED_PROCEDURE_STEP_SEQUENCE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;     
    &lt;span class="nn"&gt;AttributeAction&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;PrimitiveValue&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Empty&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// create item and add procedure step start date&lt;/span&gt;
&lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="nf"&gt;.apply&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;AttributeOp&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SCHEDULED_PROCEDURE_STEP_SEQUENCE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;tags&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SCHEDULED_PROCEDURE_STEP_START_DATE&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="nn"&gt;AttributeAction&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;SetStr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"19951015"&lt;/span&gt;&lt;span class="nf"&gt;.into&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  UID constants and SOP class dictionary
&lt;/h3&gt;

&lt;p&gt;The existence of constant declarations is a noteworthy improvement to developer experience. IDEs prepared for Rust development will index them and provide listings of constants to the developer automatically as they look for that specific attribute or abstract syntax.&lt;/p&gt;

&lt;p&gt;DICOM-rs v0.4 brought constants for DICOM tags. With DICOM-rs v0.6, we now have constants for various normative DICOM unique identifiers (UIDs), spanning from SOP classes and transfer syntaxes, to even the so-called Well-known SOP Instances. No more copying around non-descriptive string literals when wishing to grab &lt;em&gt;Patient Root Query/Retrieve Information Model - MOVE&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;dicom_dictionary_std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;uids&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// old&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;sop_class_uid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"1.2.840.10008.5.1.4.1.2.1.2"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// new&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;sop_class_uid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;uids&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PATIENT_ROOT_QUERY_RETRIEVE_INFORMATION_MODEL_MOVE&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On top of this, &lt;code&gt;dicom-dictionary-std&lt;/code&gt; also provides a run-time dictionary of SOP classes, so that applications can translate UIDs to their description. This is already in place in &lt;code&gt;dicom-dump&lt;/code&gt;, so that the &lt;em&gt;Media Storage SOP Class UID&lt;/em&gt; is presented with a human readable description next to the UID.&lt;/p&gt;

&lt;h3&gt;
  
  
  DICOM JSON support
&lt;/h3&gt;

&lt;p&gt;As the world is unquestionably connected through the World Wide Web, many medical systems already rely on DICOMweb to communicate. Much of the efforts in the project have been around working with standard DICOM data representations and the good ol' upper layer network protocols, but DICOM-rs 0.6 now brings one piece of the bridge to Web oriented development.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dicom-json&lt;/code&gt; implements DICOM JSON with the ability to serialize DICOM data into JSON and JSON data back to DICOM data. This allows for an easy translation from types found in &lt;code&gt;dicom-object&lt;/code&gt; and &lt;code&gt;dicom-core&lt;/code&gt; to the textual representation that dominates most of the web nowadays.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s"&gt;r#"{
    "00080021": { "vr": "DA", "Value":["20230610"] },
    "00200013": { "vr": "IS", "Value":["5"] }
}"#&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;InMemDicomObject&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;dicom_json&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Internally, serialization/deserialization is backed by &lt;a href="https://serde.rs" rel="noopener noreferrer"&gt;Serde&lt;/a&gt;, a mature, well-established serialization framework. An added benefit of its use is that converting DICOM data to a JavaScript value via &lt;a href="https://rustwasm.github.io/docs/wasm-bindgen/" rel="noopener noreferrer"&gt;&lt;code&gt;wasm-bindgen&lt;/code&gt;&lt;/a&gt; becomes available for free.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pixel data adapter API redesign
&lt;/h3&gt;

&lt;p&gt;One of the open challenges in DICOM-rs is transcoding DICOM data sets between transfer syntaxes. While this is mostly trivial for transfer syntaxes of non-encapsulated uncompressed pixel data, existing tools in the project will choke if they need to decode or encode the pixel data in some way, such as decoding JPEG compressed imaging data into its native pixel data form.&lt;/p&gt;

&lt;p&gt;While this will continue to be the case in v0.6.0, this version brings a more complete API for building pixel data readers and writers (also known as pixel data adapters), which are bound to transfer syntax implementations for the ability to convert pixel data in both directions. The old &lt;code&gt;PixelRWAdapter&lt;/code&gt; trait was removed and two new traits took their place, with a new set of methods which can even work with individual frames.  This change will mostly affect transfer syntax implementations and components relying on middle-level APIs. &lt;br&gt;
Support for decoding &lt;em&gt;RLE Lossless&lt;/em&gt; and decoding/encoding some JPEG-based transfer syntaxes, is already available, with more to come.&lt;/p&gt;

&lt;p&gt;With this new API just published, this makes a foundation for the next steps towards DICOM data transcoding, hopefully with the efficiency and performance that some users have come to expect.&lt;/p&gt;




&lt;p&gt;There is &lt;em&gt;a lot&lt;/em&gt; that I could cover here, but at the risk of making this post more exhausting than it already is, this is where I stop. 😅 Have a look at the &lt;a href="https://github.com/Enet4/dicom-rs/releases/tag/v0.6.0" rel="noopener noreferrer"&gt;full changelog&lt;/a&gt; for the full listings if you are interested.&lt;/p&gt;

&lt;h2&gt;
  
  
  New website
&lt;/h2&gt;

&lt;p&gt;I also took this opportunity to announce the &lt;a href="https://dicom-rs.github.io" rel="noopener noreferrer"&gt;new website for DICOM-rs&lt;/a&gt;, although it has been online for a few months. It is a seemingly simple one-page site published with GitHub Pages using &lt;a href="https://www.getzola.org" rel="noopener noreferrer"&gt;Zola&lt;/a&gt;, and contains a quick overview of the project, as well as a few examples of what it can do.&lt;br&gt;
A small surprise can be found in the middle: a live example of a DICOM dump application, which reads the user's DICOM file in the browser and shows some of its attributes.&lt;br&gt;
All source code of the website, including the live example, is available &lt;a href="https://github.com/dicom-rs/dicom-rs.github.io" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's next?
&lt;/h2&gt;

&lt;p&gt;Without replicating what's in the &lt;a href="https://github.com/Enet4/dicom-rs/wiki/Roadmap" rel="noopener noreferrer"&gt;roadmap&lt;/a&gt;, I can point out some of the things that I have in mind for the next milestones.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;As already mentioned above, an API for transcoding DICOM objects will be an early feature to focus on, possibly in the next minor release. This will then be integrated into the &lt;code&gt;storescu&lt;/code&gt; tool to enable an automatic object conversion if requested by the service class provider.&lt;/li&gt;
&lt;li&gt;The lack of high level constructs for writing DICOM network services is one of the most frequent concerns raised so far. The UL association API helps to accept and establish associations with other DICOM nodes, but there is still little hand-holding for sending and receiving commands and data in conformance with the intended DICOM Message Service Elements (DIMSE), and components to assist in compliance with the DICOM upper layer protocol state machine. There is also not a clear direction on how to make network services asynchronous, although this is in the project's plans. Making network operations non-blocking is important for a better usage of the computational resources available.&lt;/li&gt;
&lt;li&gt;Already in my thoughts, I believe that there is an opportunity to revamp DICOM object reading and construction by declaring modules from Information Object Definitions (IODs) as specially annotated Rust types. It will be a great undertaking, but it will bring immense gains to the library in multiple ways when it's done.&lt;/li&gt;
&lt;li&gt;Lazy DICOM object loading has been in my head since the beginning of the project, but it has been constantly set aside due to its difficulty to get right while maintaining usability. This still ought to be tackled sooner or later. To help drive this forward, I am likely to take it in a different direction, by first implementing a separate API for reading and writing DICOM data in independent chunks.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;Discussion is open through the respective &lt;a href="https://github.com/Enet4/dicom-rs/discussions/398" rel="noopener noreferrer"&gt;GitHub discussion thread&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>dicom</category>
    </item>
    <item>
      <title>Timely wrap-up: some (not so) quick notes on Timely Defuse</title>
      <dc:creator>Eduardo Pinho</dc:creator>
      <pubDate>Sat, 03 Dec 2022 16:32:00 +0000</pubDate>
      <link>https://dev.to/e_net4/timely-wrap-up-quick-notes-on-timely-defuse-441o</link>
      <guid>https://dev.to/e_net4/timely-wrap-up-quick-notes-on-timely-defuse-441o</guid>
      <description>&lt;h1&gt;
  
  
  Preface
&lt;/h1&gt;

&lt;p&gt;Building a video game during November has become a yearly tradition to me. Save for 2019, I've been casually participating in the GitHub Game Off game jams since 2017. And each time I do, I have so far brought along a different set of technologies to learn and apply in the development of a new video game.&lt;/p&gt;

&lt;p&gt;Before I dive in to some notes about this year's game, here is a quick history of my past works:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;GitHub Game Off 2017: &lt;a href="https://e-net4.itch.io/propan" rel="noopener noreferrer"&gt;Propan&lt;/a&gt;, written in Rust with the Piston game engine, is a 2D topdown game inspired by another game from the early 1990's, Helious.&lt;/li&gt;
&lt;li&gt;GitHub Game Off 2018: &lt;a href="https://e-net4.itch.io/pkay" rel="noopener noreferrer"&gt;Pkay&lt;/a&gt; is a pong/tower-defense hybrid made in Godot. It also featured a two-mode music loop comprising a combination of synthesised instruments with my own guitar recordings.&lt;/li&gt;
&lt;li&gt;For GitHub Game Off 2019, I had already started working on a one-button platform game with code name "vivo", but the core game mechanics were not all complete, and I got sick during that month, which made me lose valuable time. The unexpected illness and lack of motivation towards the game made me unable to finish it.&lt;/li&gt;
&lt;li&gt;Still, I grabbed Phaser.js again in the following year to make &lt;a href="https://e-net4.itch.io/win-in-space" rel="noopener noreferrer"&gt;Win in Space&lt;/a&gt;, a turn-based physics game for Game Off 2020 written in TypeScript. Fun fact: This one featured a full-length background music with the same name, and I named the game after the track rather than the other way around.&lt;/li&gt;
&lt;li&gt;Last year, for Game Off 2021, I went for quite a different vibe with a full Web experience: &lt;a href="https://e-net4.itch.io/10x-sprint-master" rel="noopener noreferrer"&gt;10x Sprint Master&lt;/a&gt; is a simulation strategy game where you pretend to be a lead software engineer, written in HTML, CSS, and Rust for WebAssembly. I wrote more about this one &lt;a href="https://dev.to/e_net4/10x-sprint-master-a-technical-and-social-experiment-ahp"&gt;here&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Introducting Timely Defuse
&lt;/h1&gt;

&lt;p&gt;In Timely Defuse, dynamites and bombs 💣 are thrown onto the game environment out of nowhere, and your job is to control the hero to defuse the dynamites and disarm the bombs before they explode. Dynamites are defused by grabbing them within reach. Bombs are disarmed by letting the hero tinker with the bomb for a few seconds 🤔. As you successfully stop these from exploding ✂, you gain points. Let bombs explode on you 🤯, and you lose points. The game proceeds in waves of increasing difficulty, as the explosives appear faster and in greater numbers. As a boost bonus, you can grab randomly thrown coffee mugs ☕ for a temporary increase of speed and disarming efficiency ⏫. After the final wave, a summary of your performance appears, including the final score 🏆. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1zvpnmctcj15hx1iwzzi.jpg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1zvpnmctcj15hx1iwzzi.jpg" alt="In-game screenshot of Timely Defuse, with a background of a orange brick wall and a grey tile grid floor, where the chubby hero is disarming a bomb. Two dynamites are also scattered about. Upper corners show the text " width="250" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conceiving the idea
&lt;/h2&gt;

&lt;p&gt;The theme for GitHub GameOff 2022 is &lt;strong&gt;cliché&lt;/strong&gt;. Participants are free to interpret it the way they want.&lt;/p&gt;

&lt;p&gt;So how did I get from &lt;em&gt;cliché&lt;/em&gt; to the game that we have here? Well, it was far from trivial. The official page provided a link to a comprehensive list of clichés, but I tried to write down a short list of my own:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;em&gt;Where do you keep your inventory?&lt;/em&gt;: a very common question in point-and-click adventures.&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Jump onto enemies to kill them&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;flappy bird clone&lt;/em&gt; (FWIW would provide a good mobile game experience)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;break things to get money&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;"press start to play"&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;mute protagonist&lt;/em&gt; (and the talkative sidekick who says everything for you, of course!)&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;miraculous food&lt;/em&gt; (have a burger, it'll seal that stab wound in no time)&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Saved at the last moment&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And not long after, I felt like reflecting deeper into the last one: a very common cliché in films and games, sometimes some characters are saved just before something bad happens. The associated TV trope is &lt;a href="https://tvtropes.org/pmwiki/pmwiki.php/Main/JustInTime" rel="noopener noreferrer"&gt;Just in Time&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So at this point I kept thinking about some mechanics in which the player had to act at the last second. I even pondered turning this into a puzzle game, where you are expected to push buttons and drag boxes until you could finally disarm a bomb one second before it explodes. However, I did not pursue this idea, for a puzzle game would have required me to devise each level manually with intricate pieces that I did not even know what they were in order to make the game interesting.&lt;/p&gt;

&lt;p&gt;By making it more action oriented, the game idea was starting to direct itself towards the player having to touch lit explosives before they blew up. Pretty much like a point-and-click reaction game. But I still did not know how to make it interesting from here.&lt;/p&gt;

&lt;p&gt;I had already tinkered with the game engine so as to understand whether I would be able to implement a mobile Web game with touch recognition easily. The answer was yes, the game engine allowed this with ease. And only after I had this answer, I came up with the idea of the player not being the one grabbing the explosives, but handing that job to an in-game protagonist instead! This made room for noteworthy mechanisms:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;By letting the player tell where the protagonist should move, actions on any object would take additional time depending on the distance between the protagonist and an object, so the player had to act fast and decide which objects to take care of first, at the risk of not being able to handle others on time.&lt;/li&gt;
&lt;li&gt;The protagonist would have a set of characteristics, namely maximum movement speed and bomb disarming efficiently, which could be temporarily boosted using power-ups.&lt;/li&gt;
&lt;li&gt;The protagonist would also be vulnerable to explosion damage, affecting the hero by blasting them away from the explosion and needing time to recover. I had thought of having a life counter which would decrease when taking a hit, but I felt that the game was fun enough to play with just the stunning effect.&lt;/li&gt;
&lt;li&gt;As a "stress" mechanism, the protagonist disarms bombs substantially faster if they are very close to exploding, making it sometimes advantageous to disarm them later.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;With this, most of the idea had been set. It was time to get my hands dirty.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical notes
&lt;/h2&gt;

&lt;p&gt;Next I will just write an assortment of topics about the development of Timely Defuse.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mobile first
&lt;/h3&gt;

&lt;p&gt;This time, after the disappointment of making a game which was not mobile friendly (also &lt;a href="https://dev.to/e_net4/10x-sprint-master-a-technical-and-social-experiment-ahp"&gt;too philosophical&lt;/a&gt;, but I digress...), I had decided that this time I wanted the game to be not only Web first, but also &lt;strong&gt;mobile first&lt;/strong&gt;. Picking a fixed resolution in (scaled) pixels which would fit on the great majority of devices seemed to do it well, although one cannot be fully certain of complete compatibility with all devices in this manner. For what it's worth, there were no complaints so far from close relatives and friends.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Bevy
&lt;/h3&gt;

&lt;p&gt;I added an extra pinch of challenge and learning opportunities by trying out the &lt;a href="https://bevyengine.org/" rel="noopener noreferrer"&gt;Bevy&lt;/a&gt; game engine. Bevy has been rising in popularity in the Rust game development circles as a simple but capable data-driven engine, with support for many platforms, including WebAssembly, backed by its own &lt;a href="https://bevyengine.org/learn/book/getting-started/ecs/" rel="noopener noreferrer"&gt;Entity Component System (ECS)&lt;/a&gt; framework. I had heard many times about ECS, even back in 2017 after I had finished Propan, but this was my first hands-on experience in this pattern.&lt;/p&gt;

&lt;h3&gt;
  
  
  Easy to bootstrap
&lt;/h3&gt;

&lt;p&gt;Setting up a barebones Bevy application was easy, especially considering that the official site provides a &lt;a href="https://bevyengine.org/learn/book/introduction/" rel="noopener noreferrer"&gt;small book&lt;/a&gt; &lt;a href="https://bevyengine.org/examples/" rel="noopener noreferrer"&gt;so many examples&lt;/a&gt; to experiment with, including ones covering full Web support. What may take longer to process is how one should develop complete projects in Bevy from start to finish, and for that we would need more references than just the book, which is still a bit thin at the moment. The &lt;a href="https://bevy-cheatbook.github.io/" rel="noopener noreferrer"&gt;Bevy Cheatbook&lt;/a&gt; ended up being a very valuable resource, which I kept some tabs open most of the time. Despite not being official, I really cannot understate how important this cheatbook was in working with Bevy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Systems, systems, syssytsetmsems
&lt;/h3&gt;

&lt;p&gt;In ECS, game logic is extended by adding &lt;em&gt;systems&lt;/em&gt;. In Bevy, systems are plain functions receiving an assortment of compatible parameters, in a way that reminds me of dependency injection systems. It was very clever of Bevy to take advantage of Rust's trait system to describe system functions. They can even describe whether components are to be queried with immutable or mutable access. Whenever we had to adjust a system function, changes to the system inclusion point were seldom needed. We would just change the signature and use it accordingly within the function.&lt;/p&gt;

&lt;p&gt;However, using ECS was not always a walk in the park.&lt;/p&gt;

&lt;p&gt;Systems can be pretty powerful in describing game logic, and I quickly ended up writing a bunch of common patterns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Removing a component from an entity after some time;&lt;/li&gt;
&lt;li&gt;Removing the whole entity after some time;&lt;/li&gt;
&lt;li&gt;Sending an event after some time;&lt;/li&gt;
&lt;li&gt;Adding a component to a specific entity after some time.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And yet, I must have overlooked something, as there didn't seem to be an easy construct for creating these kinds of systems other than just a &lt;code&gt;Timer&lt;/code&gt;. This means that a lot of these systems ended up looking like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="cd"&gt;/// system: do stuff when the time is right&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;do_stuff&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Res&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;DelayedStuff&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;delayed_stuff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;delayed_stuff&lt;/span&gt;&lt;span class="py"&gt;.timer&lt;/span&gt;&lt;span class="nf"&gt;.tick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="nf"&gt;.delta&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;delayed_stuff&lt;/span&gt;&lt;span class="py"&gt;.timer&lt;/span&gt;&lt;span class="nf"&gt;.just_finished&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// do stuff&lt;/span&gt;

            &lt;span class="c1"&gt;// destroy entity for example&lt;/span&gt;
            &lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="nf"&gt;.entity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.despawn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The repetition is something I would not encourage, but it was how I got by.&lt;/p&gt;

&lt;p&gt;Granted, it is possible to write &lt;em&gt;generic functions&lt;/em&gt; as systems. Here is a real example. Given structs &lt;code&gt;ScheduledEvent&amp;lt;E&amp;gt;&lt;/code&gt; and &lt;code&gt;DespawnOnTrigger&lt;/code&gt;, describing components:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="cd"&gt;/// component to fire an event after some time&lt;/span&gt;
&lt;span class="nd"&gt;#[derive(Component)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;ScheduledEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;timer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Timer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="cd"&gt;/// component to despawn the entity&lt;/span&gt;
&lt;span class="cd"&gt;/// when the trigger in `ScheduledEvent` is fired&lt;/span&gt;
&lt;span class="nd"&gt;#[derive(Default,&lt;/span&gt; &lt;span class="nd"&gt;Component)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;DespawnOnTrigger&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I had this system:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="cd"&gt;/// system: run scheduled events if it's their time to trigger&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;run_scheduled_events&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;'static&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Clone&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Res&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Time&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;Entity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;ScheduledEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;DespawnOnTrigger&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;event_writer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EventWriter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;scheduled_event&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;despawn_on_trigger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;query&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;scheduled_event&lt;/span&gt;&lt;span class="py"&gt;.timer&lt;/span&gt;&lt;span class="nf"&gt;.tick&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="nf"&gt;.delta&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;scheduled_event&lt;/span&gt;&lt;span class="py"&gt;.timer&lt;/span&gt;&lt;span class="nf"&gt;.just_finished&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;event_writer&lt;/span&gt;&lt;span class="nf"&gt;.send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;scheduled_event&lt;/span&gt;&lt;span class="py"&gt;.event&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;despawn_on_trigger&lt;/span&gt;&lt;span class="nf"&gt;.is_some&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="nf"&gt;.entity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="nf"&gt;.despawn&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="n"&gt;commands&lt;/span&gt;
                    &lt;span class="nf"&gt;.entity&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;entity&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                    &lt;span class="py"&gt;.remove&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;ScheduledEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="py"&gt;.remove&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;spawner&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;SpawnerCooldown&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                    &lt;span class="py"&gt;.remove&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;spawner&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;PendingThrow&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's incredible how I am just now seeing the Swiss cheese full of holes that this system is. When the &lt;code&gt;DespawnOnTrigger&lt;/code&gt; component is not present, it will remove the scheduled event component. This is so that the component doesn't stick around long after it's no longer needed, and so that it can eventually accept a new scheduled event in the future. But not only that, the function will also &lt;em&gt;remove a bunch of other components&lt;/em&gt; pertaining to specific game logic. This is not as generic as I had envisioned.&lt;/p&gt;

&lt;p&gt;Not to mention that generic systems cannot be included in the application as is. They need to be instantiated first. In this case, one for each possible event. One can imagine how easy it is to forget to instantiate the system for a new event.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="nf"&gt;.add_system_set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nn"&gt;SystemSet&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;on_update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;AppState&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;InGame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.with_system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;run_scheduled_events&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DynamiteThrownEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.with_system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;run_scheduled_events&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;BombThrownEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.with_system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;run_scheduled_events&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;CoffeeThrownEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.with_system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;run_scheduled_events&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;NextWaveEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(I would be thrilled to be told &lt;em&gt;"You're doing this wrong"&lt;/em&gt;, so long as there really is a better way!)&lt;/p&gt;

&lt;p&gt;There's more about this ECS implementation. The non-deterministic order between systems by default was also a nasty source of bugs. Passing a series of systems like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;
    &lt;span class="nf"&gt;.add_system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;progress_bar&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;clear_progress_bar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.add_system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;progress_bar&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;update_progress_bar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Means that the two systems can run in any order, and that order can even change between update frames! In this case, updating the progress bar would also make it visible, thus cancelling the effect of clearing it in the first place. I had to explicitly require one to be executed after the other.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="n"&gt;app&lt;/span&gt;
    &lt;span class="nf"&gt;.add_system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;progress_bar&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;update_progress_bar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="nf"&gt;.add_system&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nn"&gt;progress_bar&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;clear_progress_bar&lt;/span&gt;
            &lt;span class="nf"&gt;.after&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;progress_bar&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;update_progress_bar&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unless you are careful about organizing your systems from the start through labelled system sets and whatnot, inconsistencies could happen, since they can just run in any order. &lt;br&gt;
In hindsight, I would have resorted to tighter categorization of systems and possibly a much tighter execution order from the start, and perhaps relax the constraints selectively at a later stage. In any case, it feels unlikely that I would benefit a great deal from loose system order constraints in this scenario.&lt;/p&gt;

&lt;p&gt;I also read that the Bevy CheatBook recommends &lt;a href="https://crates.io/crates/iyes_loopless" rel="noopener noreferrer"&gt;&lt;code&gt;iyes_loopless&lt;/code&gt;&lt;/a&gt;, but this is something which should probably be added from the start rather than having to adapt an existing application. For me, it was too late to make that shift by the time the game was practically done.&lt;/p&gt;
&lt;h3&gt;
  
  
  My kingdom for an essential resource
&lt;/h3&gt;

&lt;p&gt;Aside from the concept of entity, component, and system, we can have resources! These are intended for things which exist globally in the application, akin to singletons. Examples of resources from the standard bevy crates include the asset loader (&lt;code&gt;AssetLoader&lt;/code&gt;), event readers and writers (&lt;code&gt;EventReader&amp;lt;E&amp;gt;&lt;/code&gt;/&lt;code&gt;EventWriter&amp;lt;E&amp;gt;&lt;/code&gt;), and the application time tracker &lt;code&gt;Time&lt;/code&gt;. Like for entities and components, they can be retrieved from any system.&lt;/p&gt;

&lt;p&gt;Remember when I mentioned that system function signatures can just be tweaked to gain access to other components? Well, this applies to system functions only. I often had functions which looked like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;spawn_dynamite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;texture_atlas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;TextureAtlas&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;bounce_sound&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Handle&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;AudioSource&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Vec3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;velocity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Vec3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Entity&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ....&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is not designed like a system because it isn't one. It is a function to be called inside another function, with the following caveats:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It had to be called with the right set of parameters. &lt;code&gt;texture_atlas&lt;/code&gt; had to be a handle the texture atlas containing the animation frames of the dynamite, and &lt;code&gt;bounce_sound&lt;/code&gt; had to be a handle to the sound that the dynamite does when it bounces. Each of these are nothing but global dependencies with &lt;strong&gt;only one possible (correct) value&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;Any dependency in this function needs to be passed along the callers. As such, &lt;em&gt;any system&lt;/em&gt; which needed to spawn a dynamite had to declare the need for a dynamite texture atlas resource and for the handles sound effects (which in the latter case I coupled together into a single resource for convenience), so that the handles could be passed here.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="cd"&gt;/// system: on dynamite thrown, spawn it&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;throw_dynamite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Commands&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ResMut&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Rng&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;texture_atlas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Res&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DynamiteTextureAtlas&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;sound_sources&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Res&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;GameSoundSources&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;event_reader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;EventReader&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;DynamiteThrownEvent&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;event_reader&lt;/span&gt;&lt;span class="nf"&gt;.iter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;random_xy_position&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;crate&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;dynamite&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;spawn_dynamite&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="n"&gt;texture_atlas&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;sound_sources&lt;/span&gt;&lt;span class="py"&gt;.thwack3&lt;/span&gt;&lt;span class="nf"&gt;.clone&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="nf"&gt;.extend&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;1200.&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="nf"&gt;random_velocity_variations&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;rng&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;event_reader&lt;/span&gt;&lt;span class="nf"&gt;.clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This was awkward in some place, where I even needed to inject not only resources but also specific query objects.&lt;/p&gt;

&lt;p&gt;It might be that one could work around this by using system chaining, something which I may have understated when working on the game.&lt;/p&gt;

&lt;h3&gt;
  
  
  UI nodes
&lt;/h3&gt;

&lt;p&gt;I can appreciate the fact that the engine comes with a user interface module, also running through ECS. With it, you can declare a graph of UI nodes by spawning entities with the right set of components so that they become frames, buttons, and so on.&lt;/p&gt;

&lt;p&gt;Timely Defuse was thin on UI, but it was helpful to have a straightforward way to make text appear in the way that was predictable. The use of &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout" rel="noopener noreferrer"&gt;Flexbox&lt;/a&gt; from the CSS world to describe node styling was peculiar, but a good decision overall. It does not mean that you will always get the presentation that you hoped for the first try, but heh.&lt;/p&gt;

&lt;h3&gt;
  
  
  Safety obstacles
&lt;/h3&gt;

&lt;p&gt;I wouldn't expect to stumble upon such a problem, but it may happen that the game starts with no audio. The latest browser security mechanisms imposes that audio contexts must only be created after user interaction. This means that if you enter the game page through GitHub without clicking on anything, you might get no sound at all.&lt;/p&gt;

&lt;p&gt;I did some checking and it's true that even my other Web based submission, &lt;a href="https://enet4.github.io/win-in-space/" rel="noopener noreferrer"&gt;Win in Space&lt;/a&gt;, will only have sound and music playing as soon as I interact with the page. The difference was that Phaser.js has a mechanism to resume the existing audio context if it was create too soon to be able to engage, but Bevy as of v0.9.0 does not. Maybe I should file an issue.&lt;/p&gt;

&lt;h3&gt;
  
  
  Compilation times
&lt;/h3&gt;

&lt;p&gt;Bevy boasts having achieved quick compilation times in development mode in comparison with other frameworks, going as far as to suggest that &lt;em&gt;"you can expect 0.8-3.0 seconds with the "fast compiles" configuration"&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;It is good that the project goes to great lengths to reduce compile times. However, to test the WebAssembly build, some of the options suggested to improve compilation performance are not available. With &lt;code&gt;opt-level = 1&lt;/code&gt; and dynamic linking, it would take at least 10 seconds to build this application with a single change to the source code on my 2019 laptop. I could only get the alleged compilation times by only optimizing the dependencies and leaving the end crate at opt-level 0. The other suggestions to change the linker may be also important, but again, they may be out of reach when working for the Web.&lt;/p&gt;

&lt;p&gt;Ultimately, optimization options for release were focused on reducing size. The WebAssembly file for production was 17 MiB large. In-game performance was mostly OK.&lt;/p&gt;

&lt;h1&gt;
  
  
  Conclusion
&lt;/h1&gt;

&lt;p&gt;As typical in a GitHub Game Off submission, the &lt;a href="https://github.com/Enet4/timely-defuse" rel="noopener noreferrer"&gt;full source and assets are available&lt;/a&gt;. Many of the graphics and sound effects were retrieved and adapted from free sources. You can play the game in a modern browser, using a mobile device or desktop, &lt;a href="https://enet4.github.io/timely-defuse/" rel="noopener noreferrer"&gt;from GitHub&lt;/a&gt; or &lt;a href="https://e-net4.itch.io/timely-defuse" rel="noopener noreferrer"&gt;from itch.io&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Just keep defusing and nobody explodes.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>gamedev</category>
      <category>githubgameoff</category>
      <category>bevy</category>
    </item>
    <item>
      <title>Writing bindings to `dos-like` for Rust: some lessons learned</title>
      <dc:creator>Eduardo Pinho</dc:creator>
      <pubDate>Tue, 15 Mar 2022 18:23:53 +0000</pubDate>
      <link>https://dev.to/e_net4/writing-bindings-to-dos-like-for-rust-some-lessons-learned-2p6k</link>
      <guid>https://dev.to/e_net4/writing-bindings-to-dos-like-for-rust-some-lessons-learned-2p6k</guid>
      <description>&lt;p&gt;My childhood included a fair share of MS-DOS games from the early to mid 1990's. And even decades later, I appreciate looking back to the games and studying the technologies surrounding them, such as Sound Blaster audio and the various VGA graphics modes of the time, honorably mentioning &lt;a href="https://en.wikipedia.org/wiki/Mode_13h" rel="noopener noreferrer"&gt;mode 13h&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Now, being both a Rust enthusiast and MS-DOS nostalgic, I have, multiple times, tried closing the gap on writing applications for real DOS systems in Rust. Unfortunately, this is not without issues, and there is not a clear path on how to go with this yet. More on existing efforts &lt;a href="https://github.com/Serentty/rusty-dos/issues/3" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So when I had some spare time the past weekend, I decided to do something a bit different: bring to Rust an existing framework that lets you write applications which look like they are DOS applications.&lt;br&gt;
&lt;a href="https://github.com/mattiasgustavsson/dos-like" rel="noopener noreferrer"&gt;&lt;code&gt;dos-like&lt;/code&gt;&lt;/a&gt;, made by Mattias Gustavsson, is like a small engine for writing modern applications with the look &amp;amp; feel of MS-DOS programs. So basically, when using this framework, we end up with applications that run on modern hardware and operating systems all the same, but with deliberate video effects and audio that bring us back to that era, including large pixels, CRT distortion, text and graphics video modes, and synthesized (Sound Blaster 16) or MIDI (Sound Blaster AWE32) music. It was written in C, mostly as a single file with some other statically linked dependencies. The project also comprises a few fun examples, such as a proof-of-concept FPS inspired by Wolfenstein 3D, a point-and-click adventure, a voxel renderer, and even a music tracker.&lt;/p&gt;

&lt;p&gt;By creating direct bindings to the C interface (&lt;code&gt;dos-like-sys&lt;/code&gt;), followed by a more high-level abstraction, it becomes possible and (hopefully) intuitive to write applications of this sort in Rust! &lt;a href="https://github.com/Enet4/dos-like-rs" rel="noopener noreferrer"&gt;So I did that!&lt;/a&gt;&lt;br&gt;
In this post, I will now share a small collection of technical topics that I felt worth sharing about the conception of these bindings.&lt;/p&gt;
&lt;h3&gt;
  
  
  Compiling and static linking
&lt;/h3&gt;

&lt;p&gt;Although I've had the experience of &lt;a href="https://medium.com/@e_net4/taking-the-load-road-part-2-rust-bindings-for-a-vector-similarity-search-library-914fbc1ec5ed" rel="noopener noreferrer"&gt;writing bindings to dynamically linked libraries&lt;/a&gt;  in the past, this one blatantly called for direct static linking to a C object compiled on the spot. By following the documentation of &lt;a href="https://crates.io/crates/cc" rel="noopener noreferrer"&gt;&lt;code&gt;cc&lt;/code&gt;&lt;/a&gt; and observing some &lt;code&gt;build.rs&lt;/code&gt; files from other bindings, I managed to make compilation and linking work on Linux and Windows, straight from the original sources, fetched using a git submodule. A few extra steps were needed on Linux, since it depends on SDL2.&lt;/p&gt;

&lt;p&gt;Alas, although there is WebAssembly support in the original &lt;code&gt;dos-like&lt;/code&gt;, it is still not supported in the bindings for Rust. It would require a Rust toolchain to integrate with &lt;a href="https://github.com/schellingb/wajic" rel="noopener noreferrer"&gt;WAjic&lt;/a&gt;, which I am pretty much unfamiliar with. If you have any idea on how to achieve this, I would love to know.&lt;/p&gt;
&lt;h3&gt;
  
  
  Is this a library?
&lt;/h3&gt;

&lt;p&gt;One of the most interesting things about the final &lt;code&gt;dos-like&lt;/code&gt; package delivered to crates.io is that, while it is indeed declared as a Rust library, it has a strong caveat that needs to be considered whenever it is added to a project: it &lt;em&gt;provides its own &lt;code&gt;main&lt;/code&gt; function&lt;/em&gt;!&lt;/p&gt;

&lt;p&gt;What makes &lt;code&gt;dos-like&lt;/code&gt; so easy to create applications is that it takes care of all application bootstrapping by itself. When writing a new program in C or C++, this is what would happen:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;In a new C file, the user includes &lt;code&gt;dos.h&lt;/code&gt; and writes a &lt;code&gt;main&lt;/code&gt; function definition as usual.&lt;/li&gt;
&lt;li&gt;That C file is linked alongside &lt;code&gt;dos.c&lt;/code&gt;, which reassigns the &lt;code&gt;main&lt;/code&gt; function to a function named &lt;code&gt;dosmain&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The framework replaces the &lt;code&gt;main&lt;/code&gt; function definition with its own function, which does, among other things, call &lt;code&gt;dosmain&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;In a plain C environment, it is possible to do this kind of symbol reassignment with macros. But in Rust, there are no C macros! The library has no way of knowing that we have declared a main function in C land! As a consequence, the Rust linker would find two main function declarations and fail to link.&lt;/p&gt;

&lt;p&gt;So the solution to making this work is this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add the &lt;code&gt;no_main&lt;/code&gt; attribute, so that Rust does not try, nor demand you to declare a main function;&lt;/li&gt;
&lt;li&gt;Declare an extern C function called &lt;code&gt;dosmain&lt;/code&gt; instead.
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#![no_main]&lt;/span&gt;

&lt;span class="k"&gt;extern&lt;/span&gt; &lt;span class="s"&gt;"C"&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;dosmain&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;i32&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="mi"&gt;0&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;I also added a Rust macro to assist in this last declaration, but I don't find it very usable nor idiomatic.&lt;/p&gt;

&lt;p&gt;Since the bottom part of the program is a C function, stack unwinding also does not work. I chose to recommend users to abort on panic instead, by writing this in their Cargo.toml file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight toml"&gt;&lt;code&gt;&lt;span class="nn"&gt;[profile.dev]&lt;/span&gt;
&lt;span class="py"&gt;panic&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"abort"&lt;/span&gt;

&lt;span class="nn"&gt;[profile.release]&lt;/span&gt;
&lt;span class="py"&gt;panic&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"abort"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Is that an &lt;code&gt;int&lt;/code&gt;?
&lt;/h3&gt;

&lt;p&gt;The majority of functions in the framework were pretty easy to translate to Rust. With the low-level bindings already built automatically from the C declarations via &lt;a href="https://github.com/rust-lang/rust-bindgen" rel="noopener noreferrer"&gt;&lt;code&gt;bindgen&lt;/code&gt;&lt;/a&gt;, all that was left was to encapsulate them in non-&lt;code&gt;unsafe&lt;/code&gt; functions with idiomatic parameter types. A great deal of these functions received plain C &lt;code&gt;int&lt;/code&gt;s as parameters. Instead of those, the parameter types were chosen based on what they represented:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;u8&lt;/code&gt; is great for color identifiers, since the application is limited to a 256 color palette. It was also used for the RGB value components of each color, in which functions expected integers between 0 and 255 anyway (although in the end they would be artificially trimmed to a precision of 6 bits per color, thus replicating the 18-bit palette of the time).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;u16&lt;/code&gt; was used for x and y coordinates, both in graphics mode and in text mode. 16 bits was a pretty common integer size back then, and the screen width or height would never be larger than this.&lt;/li&gt;
&lt;li&gt;Where a 0 or 1 were expected for stating whether something should be &lt;em&gt;on&lt;/em&gt; or &lt;em&gt;off&lt;/em&gt;, a &lt;code&gt;bool&lt;/code&gt; is used instead. Easy peasy.&lt;/li&gt;
&lt;li&gt;Some functions expected a C string, as in a pointer to a null-terminated sequence of characters. Creating a function that receives a standard Rust string slice &lt;code&gt;&amp;amp;str&lt;/code&gt; requires a new string to be allocated with a null character &lt;code&gt;\0&lt;/code&gt; appended at the end. To assist those who might already have a null-terminated string in handy, a separate function was created that receives a slice of &lt;a href="https://doc.rust-lang.org/stable/std/ffi/struct.CStr.html" rel="noopener noreferrer"&gt;&lt;code&gt;CStr&lt;/code&gt;&lt;/a&gt; instead.&lt;/li&gt;
&lt;li&gt;Certain resource identifiers were replaced with their high level abstraction, such as the &lt;code&gt;Soundbank&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since I often had to look into how each function was implemented, this was also an opportunity to &lt;a href="https://docs.rs/crate/dos-like" rel="noopener noreferrer"&gt;document them&lt;/a&gt;. Maybe one day someone can pass along some of these texts into the original &lt;code&gt;dos-like&lt;/code&gt;, if they still apply there.&lt;/p&gt;

&lt;h3&gt;
  
  
  Exclusive, safe video memory access? Pick one.
&lt;/h3&gt;

&lt;p&gt;Now, &lt;code&gt;dos-like&lt;/code&gt; is pretty usable with the various drawing primitives, such as &lt;code&gt;line&lt;/code&gt;, &lt;code&gt;bar&lt;/code&gt;, &lt;code&gt;blit&lt;/code&gt;, &lt;code&gt;circle&lt;/code&gt;, &lt;code&gt;outtextxy&lt;/code&gt;, ... But &lt;code&gt;dos-like&lt;/code&gt; also gives you a way to directly manipulate the video memory buffer to be written to the screen:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;screenbuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output is a plain pointer to &lt;code&gt;width * height&lt;/code&gt; bytes. If double buffering is enabled, &lt;code&gt;swapbuffers&lt;/code&gt; passes that data to the display and gives you another buffer to write to. Both buffers are pieces of memory created once at boot, the library just gives one or the other in turns, on each call to &lt;code&gt;swapbuffers&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;swapbuffers&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These functions are often used in practice like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;setvideomode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;videomode_320x200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;setdoublebuffer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;screenbuffer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;shuttingdown&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;waitvbl&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// write things to buffer&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;320&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// swap buffer&lt;/span&gt;
    &lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;swapbuffers&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// ... handle user input&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, so long as you do not write beyond the boundaries of any of the given memory buffers, you are safe and have nothing to worry about. But how can we make this both &lt;strong&gt;easy&lt;/strong&gt; and &lt;strong&gt;safe&lt;/strong&gt; to use in Rust?&lt;/p&gt;

&lt;p&gt;The easiest way to use a writable memory buffer in Rust is with a mutable byte slice: &lt;code&gt;&amp;amp;mut [u8]&lt;/code&gt;. We could think of a function done like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;unsafe&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;screen_buffer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;'static&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nb"&gt;u8&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
   &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;get_screen_resolution&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
   &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;slice&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from_raw_parts_mut&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;screenbuffer&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;len&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But this is &lt;em&gt;completely memory unsafe&lt;/em&gt;! Mutable slices expect exclusive access to that piece of memory, with &lt;a href="https://doc.rust-lang.org/nomicon/aliasing.html" rel="noopener noreferrer"&gt;no aliasing&lt;/a&gt; of any kind. Unlike raw pointers, having more than one mutable slice to this video buffer is instant &lt;em&gt;undefined behavior&lt;/em&gt;. And this function makes that as easy as calling it twice without swapping the buffers first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;unsafe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;screen_buffer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;buffer_copy&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;screen_buffer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// UB :[&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In addition, calling any other drawing function while we have a hold of this slice is undefined behavior as well.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;unsafe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;screen_buffer&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;circle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;120&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;100&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// also UB :[&lt;/span&gt;
    &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I did try to think of other ways to make exclusive access verified at compile time, such as creating a static global instance with a phantom lifetime to serve as a lock. But this wasn't enough to simulate double buffering, as it would prevent a legitimate use of swapping buffers and fetching a slice to the new buffer. Even with both functions returning mutable slices, they would be exclusive, and it would be safe to use the same variable, since a reassignment would drop the previous slice before there is a chance for aliased slices:&lt;br&gt;
&lt;/p&gt;

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

&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;unsafe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;screen_buffer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// slice to buffer0&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;shutting_down&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;wait_vbl&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// use buffer&lt;/span&gt;

    &lt;span class="n"&gt;buffer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;unsafe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;swap_buffers&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="c1"&gt;// slice to buffer1 (iteration #0)&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt; &lt;span class="c1"&gt;// slice to buffer0 (iter #0) dropped immediately&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A safe abstraction for access to the two video buffers (let's call them &lt;code&gt;buffer0&lt;/code&gt; and &lt;code&gt;buffer1&lt;/code&gt;) would:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;allow the user to retain at most one mutable slice to &lt;code&gt;buffer0&lt;/code&gt; and at most one mutable slice to &lt;code&gt;buffer1&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;let the user seamlessly switch between the two in the main application loop, with a reassignment or something equally usable;&lt;/li&gt;
&lt;li&gt;AND prevent calls to any other drawing primitives for as long as any of the slices are held.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I couldn't come up with this yet, but feel free to reach out if you have a better idea!&lt;/p&gt;

&lt;p&gt;In the end, the two functions were exposed as &lt;code&gt;unsafe&lt;/code&gt;, with &lt;a href="https://docs.rs/dos-like/latest/dos_like/video/fn.screen_buffer.html" rel="noopener noreferrer"&gt;detailed safety guidelines&lt;/a&gt; documented. Those who which to avoid direct video buffer access can still use the various drawing primitives and achieve the same thing with only a bit of extra overhead, which seems unlikely to become a problem in my opinion.&lt;/p&gt;

&lt;p&gt;I am also not sure whether any of this is memory safe with double buffering disabled. Oh boy.&lt;/p&gt;

&lt;h3&gt;
  
  
  Returning small arrays? One simple trick.
&lt;/h3&gt;

&lt;p&gt;With the elephant in the room pointed out, let's go back to easier challenges. We also have a few functions to provide a list of user input events:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="n"&gt;keycode_t&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;readkeys&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;unsigned&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="nf"&gt;readchars&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The returned pointers are also null-terminated, where the first is for key codes and the second one is for characters. For instance, if the user held &lt;code&gt;Shift&lt;/code&gt; and pressed &lt;code&gt;A&lt;/code&gt;, the first function would give us the &lt;code&gt;SHIFT&lt;/code&gt; key code and the &lt;code&gt;A&lt;/code&gt; key code, whereas &lt;code&gt;readchars&lt;/code&gt; would give us an upper-case &lt;code&gt;A&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Keyboard and mouse events are accumulated in an internal buffer. When one of these functions are called, the recorded keycodes or characters are flushed into a separate buffer and a pointer to that buffer is returned. One may feel tempted to write a high-level function with this signature:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;read_keys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;'static&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;KeyCode&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But doing this would &lt;em&gt;not&lt;/em&gt; be memory safe! Rust requires that data behind a reference never mutate, but with subsequent calls to &lt;code&gt;read_keys&lt;/code&gt;, the contents in the same buffer could change as part of the implementation.&lt;br&gt;
On the other hand, unlike &lt;code&gt;screenbuffer&lt;/code&gt;, this pointer is only intended to be read from, and as such it is a fairly good candidate for copying over to an owned vector. Here is the complete implementation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;read_keys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Vec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;KeyCode&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="k"&gt;mut&lt;/span&gt; &lt;span class="n"&gt;keys&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;Vec&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;unsafe&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;p&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;dos_like_sys&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;readkeys&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="o"&gt;..=&lt;/span&gt;&lt;span class="mi"&gt;255&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;p&lt;/span&gt;&lt;span class="nf"&gt;.offset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="n"&gt;keys&lt;/span&gt;&lt;span class="nf"&gt;.push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;KeyCode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;keys&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A copy is free to be read and manipulated in consumer space without any risk of breaking memory invariants. Unless you're into keyboard smashing or only rarely call these functions, we expect them to return a very small number of events each time, often 0 to 2 values, making the copy pretty fast anyway. Moreover, if we replace &lt;code&gt;Vec&amp;lt;_&amp;gt;&lt;/code&gt; with, say, a &lt;a href="https://docs.rs/small_vec/0.1.2/small_vec" rel="noopener noreferrer"&gt;&lt;code&gt;small_vec&lt;/code&gt;&lt;/a&gt;, we're avoiding heap allocations when the number of events in queue does not justify it. The final signature of the high level function is the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;read_keys&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;SmallVec&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;KeyCode&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Validated resource identifiers
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;dos-like&lt;/code&gt; allows you to load custom fonts from .fnt files for text output in graphics mode, and custom soundbanks for synthesized music. When the functions below succeed, they save those resources in the framework and return a positive integer which identifies that resource for the rest of the application's lifetime.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;installuserfont&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nf"&gt;installusersoundbank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;char&lt;/span&gt;&lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some functions would then accept an integer to identify the font or soundbank to use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight c"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;settextstyle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;italic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;underline&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;setsoundbank&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;soundbank&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Technically, these identifiers are checked by the implementation, so it is not undefined behavior to pass an invalid ID. Still, a type safe abstraction around it prevents misuse by expecting an instance to be created first.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;font&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;install_user_font&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"files/volter.fnt"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nf"&gt;set_text_style&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This may seem oddly equivalent to what would be done in C, but there's a neat safeguard in the function signatures:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;install_user_font&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="nb"&gt;AsRef&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Font&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;FileError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;set_text_style&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;font&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Font&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;bold&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;italic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;underline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The first one returns an identifier of type &lt;code&gt;Font&lt;/code&gt;, only if the operation was successful. The second one will not accept anything else other than a font identifier, all of this checked at compile time. And an instance of this type can be passed just as easily as an &lt;code&gt;int&lt;/code&gt;, because it's just a &lt;code&gt;NonZeroU32&lt;/code&gt; underneath! A &lt;a href="https://doc.rust-lang.org/stable/std/num/struct.NonZeroU32.html" rel="noopener noreferrer"&gt;&lt;code&gt;NonZeroU32&lt;/code&gt;&lt;/a&gt; is an unsigned 32 bit integer which is sure to never be zero. The compiler can take advantage of this to represent derivative types in less bytes (i.e. &lt;code&gt;Option&amp;lt;NonZeroU32&amp;gt;&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;For other resources, such as music and sound, such resource loading functions would yield their output via returned pointers to static memory or by output pointers. &lt;code&gt;Sound&lt;/code&gt;, &lt;code&gt;Music&lt;/code&gt;, and &lt;code&gt;Image&lt;/code&gt; were created with an object oriented design. One advantage of this is that some of the functions can be intuitively made into methods.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;music&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;load_opb&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"files/doom.opb"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// play music, no looping, maximum volume&lt;/span&gt;
&lt;span class="n"&gt;music&lt;/span&gt;&lt;span class="nf"&gt;.play&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;255&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;p&gt;So this was an aggregation of tricks through which a C framework can be suited for writing applications in Rust. &lt;code&gt;dos-like&lt;/code&gt; for Rust is freely available &lt;a href="https://crates.io/crates/dos-like" rel="noopener noreferrer"&gt;on crates.io&lt;/a&gt; and on &lt;a href="https://github.com/Enet4/dos-like-rs" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. The GIF below shows an example that can be written in 60 lines of Rust code.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdfif3rmbsj6ntotbpfb0.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdfif3rmbsj6ntotbpfb0.gif" alt="Rotozoom example with Ferris" width="640" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>rust</category>
      <category>msdos</category>
      <category>showdev</category>
      <category>gamedev</category>
    </item>
    <item>
      <title>10x Sprint Master: a technical and social experiment</title>
      <dc:creator>Eduardo Pinho</dc:creator>
      <pubDate>Sat, 18 Dec 2021 18:49:31 +0000</pubDate>
      <link>https://dev.to/e_net4/10x-sprint-master-a-technical-and-social-experiment-ahp</link>
      <guid>https://dev.to/e_net4/10x-sprint-master-a-technical-and-social-experiment-ahp</guid>
      <description>&lt;p&gt;I recently wrote a game for the &lt;a href="https://itch.io/jam/game-off-2021" rel="noopener noreferrer"&gt;GitHub GameOff&lt;/a&gt; jam, which has been a fairly common practice of mine for a while. &lt;a href="https://e-net4.itch.io/10x-sprint-master" rel="noopener noreferrer"&gt;&lt;strong&gt;10x Sprint Master&lt;/strong&gt;&lt;/a&gt; is about software development, team coordination, quality assurance, bug fixing, workplaces, and taking breaks for coffee every once in a while. In other words, this game is about work. The tech work that so many developers out there have, except that it's intended to be fun.&lt;/p&gt;

&lt;p&gt;The next section of this post is a description of the game. Afterwards, there will be a more technical section, in which I will cover the technologies that were used, as well as personal notes and expectations about their use. Finally, the third section makes an introspection into the social aspects that give the game a personality of its own.&lt;/p&gt;

&lt;h2&gt;
  
  
  Description
&lt;/h2&gt;

&lt;p&gt;The game puts &lt;em&gt;You&lt;/em&gt; in the shoes of a &lt;strong&gt;development lead engineer&lt;/strong&gt;, who has just arrived into a fictional company, and will soon become responsible for creating tasks to perform, specifying what each task entails, and assigning them to members of the team, while managing the life cycle of each task.&lt;/p&gt;

&lt;p&gt;You don't have a name (the caption "You" appears below your avatar), but you are given the opportunity to name the product or project to whatever you want (even though the company is nameless). And oddly enough, all your team members have actual names.&lt;/p&gt;

&lt;p&gt;The entirety of the game takes place in front of a &lt;em&gt;project workboard&lt;/em&gt;, consisting of 5 columns, each representing a stage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;em&gt;Backlog&lt;/em&gt; stage is where new tickets arrive.&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;Sprint Candidate&lt;/em&gt; stage turns stub tickets into well specified tasks.&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;In Progress&lt;/em&gt; stage is where the actual coding takes place to fulfill the task's acceptance criteria.&lt;/li&gt;
&lt;li&gt;The &lt;em&gt;Under Review&lt;/em&gt; stage is where tasks are checked for bugs. If bugs are found, moving them back to In Progress allows you to fix them.&lt;/li&gt;
&lt;li&gt;Once ready to go upstream, tasks are moved into the &lt;em&gt;Done&lt;/em&gt; stage, and can no longer be interacted with.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As you start the game, and if you kept the &lt;em&gt;Onboarding&lt;/em&gt; option checked, you will meet with the former lead engineer, who will explain you how to do things. But that mentor will also leave you at the end of the month, and so you will be forced to inherit the entire knowledge and workload during that time! To the player though, it amounts to knowing how to drag and drop tickets around with the mouse.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkqa3zwrt8vxjfx3m7kz1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkqa3zwrt8vxjfx3m7kz1.png" alt="In-game screenshot: Month 3, two developers" width="800" height="692"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You start the game severely understaffed: only you as the developer. A few months in and new hires arrive. However, they will not have the same experience as you, so they will work slower until they get better over time. And speaking of time, some tasks have a deadline, and sometimes you are given some final hour feature requests.&lt;/p&gt;

&lt;p&gt;Every while that a developer writes code under a task, they may be introducing bugs. 🐛 The review stage is the place where they are hopefully found. 👀 ...that is, before it is too late and they are merged. Irrespective of that, each contribution brought upstream increases the complexity of the software, which can be mitigated with chore tasks. As time passes, the tasks to fulfill become more and more demanding.&lt;/p&gt;

&lt;p&gt;Occasionally, you will receive random messages from your peers for the full workplace experience. They will complain during their coffee breaks ☕ about the excessive number of meetings. You will receive reminders to pick the toppings for your pizza lunch. 🍕 You may also be asked to help the DevOps team, which is almost always in trouble. &lt;/p&gt;

&lt;p&gt;And yet, the game actually does not mention how much these developers get paid. There is no salary negotiation, no distribution of dividends, not even a worthy mention of how well received your product is.&lt;/p&gt;

&lt;p&gt;What exactly drives the player to continue going, then? Well, there is a score. Merge tasks, and it goes up. Mistreat your software with too many bugs and technical debt, and it will slowly linger over time. Make it too difficult to work on your projects, and your team members will give up and leave, only making things worse. Ultimately, optimizing for score requires you to strike a balance between feature delivery, bug fixing, and technical debt mitigation.&lt;/p&gt;

&lt;p&gt;Do this well enough and you will receive a special call from the CEO! Well, this might not actually be possible though. Please let me know if you ever manage to achieve this.&lt;/p&gt;

&lt;h2&gt;
  
  
  A technical experiment
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Primary tooling
&lt;/h3&gt;

&lt;p&gt;I decided to write this game in HTML, CSS, and... that's right, Rust via WebAssembly.&lt;/p&gt;

&lt;p&gt;It was never a matter of whether these were the right tools for the job. It was about gaining experience in a field which I only had minimal grasp of, and about understanding how far one could go in frontend development for the Web using Rust, a programming language which I enjoy and am already well fared in outside web front-end development.&lt;/p&gt;

&lt;p&gt;In the process, I picked Yew as the Web framework. There wasn't too much thought put on this decision, but I felt that it was the most mature Rust solution for dynamic web applications. Trunk was used for building the project, which is apparently a common choice here. It is far from being as complete as Webpack or Parcel, but it did the job as intended:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;trunk serve&lt;/code&gt; would continuously build and refresh open pages on any source changes;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;trunk build&lt;/code&gt; would do the process once.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Combine that with additional options and Cargo tweaks, and that would give me all resources, optimized, hashed and ready to be published.&lt;/p&gt;

&lt;p&gt;There was only one &lt;strong&gt;relevant tweak&lt;/strong&gt; I had to make in the distributed index HTML file in order to make it work on itch.io: the paths to the various assets had to be modified to be &lt;em&gt;relative paths instead of absolute paths&lt;/em&gt;. This is because itch.io provides the games' assets in a cross origin which will not sit in the root path.&lt;/p&gt;

&lt;h3&gt;
  
  
  Graphics and design
&lt;/h3&gt;

&lt;p&gt;I have also been taking &lt;a href="https://css-for-js.dev" rel="noopener noreferrer"&gt;Josh Comeau's CSS for JavaScript developers course&lt;/a&gt;, and I took this jam as an opportunity to apply my new skills. Even though I have only completed the first few modules, it has so far enabled me to have a much better grasp of CSS, instead of just blindly messing around with properties until I got what I wanted. If you are interested in knowing how to write modern CSS, there is no other course I could recommend but this one.&lt;/p&gt;

&lt;p&gt;As such, the game's design was primarily driven by CSS, with some emojis to serve as memorable icons. Even the characters and the cute little bug presented in the tickets were made with a bunch of styled &lt;code&gt;div&lt;/code&gt;s. Keeping that part of the application away from Rust felt ideal, so as to reduce DOM manipulations to the minimum necessary. Even the animations in the onboarding phase demonstrating how to move tasks around were completely devoid of JavaScript or WebAssembly to function. I liked them pretty much.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flzmq7c635sywtqudmurl.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/cdn-cgi/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Flzmq7c635sywtqudmurl.gif" alt="In-game screenshot depicting one of the messages during the on-boarding phase." width="800" height="701"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There's even an easter egg: stay in the main menu for a while to see something cute. All in pure CSS. 😬&lt;/p&gt;

&lt;p&gt;I added PostCSS so that I could use &lt;code&gt;@import&lt;/code&gt; statements and incorporate automatic prefixing. One of the minor nuisances that I could not resolve was to join PostCSS and Trunk together when serving the application locally. I ended up creating an npm project with multiple scripts to run all the necessary steps, and I would just run a CSS watcher alongside &lt;code&gt;trunk serve&lt;/code&gt; in another terminal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Using Rust
&lt;/h3&gt;

&lt;p&gt;But to speak of nicer things now. Thanks to the use of Rust, working on certain features was a breeze.&lt;/p&gt;

&lt;h4&gt;
  
  
  Game state
&lt;/h4&gt;

&lt;p&gt;The game's state was serialised and deserialised back using &lt;code&gt;serde&lt;/code&gt;, achieved with little more than a few automatically derived trait implementations. With the use of a textual representation, it was possible to rely on the standard Web &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage" rel="noopener noreferrer"&gt;local storage&lt;/a&gt; to save the game. It wouldn't always work when hosted on itch.io, unfortunately. Browsers are finicky when you depend on storage for a cross-origin resource, and might block it completely. I had to tell players to disable their browser shields in order to save their progress.&lt;/p&gt;

&lt;p&gt;There were a few other pieces of information which were passed around in a serialised form through the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTML_Drag_and_Drop_API" rel="noopener noreferrer"&gt;Drag and Drop API&lt;/a&gt;. This in particular felt like a great achievement, as I had to include a custom helper abstraction for access to data transfer properties. This is what made dragging and dropping work. Alas, this API would have required a lot of work in order to support touch events and other forms of feedback that I was hoping to obtain, but were not possible through data transfer alone.&lt;br&gt;
The constraints caused by this were:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The game does not work on mobile devices, although this support would have been wonderful to have.&lt;/li&gt;
&lt;li&gt;Feedback about an operation would only appear after the fact: instead of the workboard stage column appearing with a different style when hovering a task over it, I added a message in red at the top of the workboard whenever the player tried to make an invalid move for a task , such as from &lt;em&gt;Backlog&lt;/em&gt; directly to &lt;em&gt;Done&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Random stuff
&lt;/h4&gt;

&lt;p&gt;Random events of various sorts were triggered with the help of &lt;code&gt;rand&lt;/code&gt;. I found &lt;code&gt;rand_pcg&lt;/code&gt; to be nice and fast for the purpose. The ugly story here is that the odds of something happening at a game update tick (be it whether a bug is introduced, a new task should appear, etc.) was achieved with a mishmash of formulae combining a variety of in-game parameters in a very creative way. This came with the problem that they were quite hard to tune, 🔧 which in a way contributed to the game's notably high difficulty. Note to self: create better abstractions for random events next time.&lt;/p&gt;

&lt;p&gt;With these events and other game entities generated, the elegance of Rust enabled me to manipulate the game's task in ways which I was already well versed in. Iterators were employed throughout when transforming tasks and updating state based on them.&lt;/p&gt;

&lt;h4&gt;
  
  
  Yew
&lt;/h4&gt;

&lt;p&gt;Last but definitely not least, I will talk about Yew. &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It required me to perform many more &lt;code&gt;.clone()&lt;/code&gt;s than the ones I would usually do. This is mostly because component properties had to be owned by each instantiation, although I did not get into whether they this was &lt;em&gt;always&lt;/em&gt; necessary. In any case, there were a significant bunch of clones.&lt;/li&gt;
&lt;li&gt;The macro based syntax for declaring virtual DOM nodes did the job, but it was quite verbose and a bit tricky to get the hang of. It was annoying that all hardcoded text strings inside had to be enclosed in quotes &lt;em&gt;as well as&lt;/em&gt; an expression block (e.g. &lt;code&gt;&amp;lt;div&amp;gt;{"Something"}&amp;lt;\div&amp;gt;&lt;/code&gt;), although this may have to do with a limitation of the macro system. As part of a macro, syntax highlighting was constantly broken.&lt;/li&gt;
&lt;li&gt;It further reinforced my idea that &lt;em&gt;working with callbacks in Rust is a mess&lt;/em&gt;. It is already a paradigm which I strive to avoid, but there is no way to avoid it here. Closures had to capture context in a special way, and interfacing with the JavaScript environment required a bunch of conversions to make things work. There were helper methods to create callbacks interfacing with Yew, but it was still a bit surprising when the program failed to compile once I tried to use some component state without cloning it beforehand. That pattern became clearer, but it still put me to the test of how much I knew about ownership, borrowing, and non-lexical lifetimes.&lt;/li&gt;
&lt;li&gt;It was not always clear how one is expected to manage the game's state and trigger changes from user interactions down the tree of elements. I ended up implementing a global event dispatcher that the main game component would listen and handle immediately (e.g. for pop-up messages) or pass on to a game update routine. It was a weird piece, and the primary game component was much more complex than the other ones because of it, but it did just what I was looking for.&lt;/li&gt;
&lt;li&gt;It was fast enough. It would have been nice for me to say that it was blazing fast, but I don't have a point of comparison. That would require me to rewrite the whole thing in JavaScript. 🤢 Still, even though the DOM had to be updated around once every 50 ms, this was &lt;em&gt;far&lt;/em&gt; from becoming a problem. Most updates would take less than 3 ms each (compiled in release mode) in my personal laptop, 9 ms in debug mode worst case.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The &lt;a href="https://github.com/Enet4/10xSprintMaster" rel="noopener noreferrer"&gt;source code&lt;/a&gt; is available on GitHub, as is typical in GitHub GameOff. Enjoy.&lt;/p&gt;

&lt;h2&gt;
  
  
  A social experiment
&lt;/h2&gt;

&lt;p&gt;The idea of making such a nerdy strategy game that puts you in the role of a software engineer wasn't very new. I had kept a similar idea for the right time to work on it, but 10x Sprint Master, albeit just as nerdy an idea as I could come up with, was on a whole different subject: treating the project workboard as the center of all attention in software engineering.&lt;/p&gt;

&lt;p&gt;It is worth noting that this game was never meant to make an accurate portrayal of any well known agile methodology, such as Scrum. There may indeed be similarities, but ultimately, this is not Scrum, and I definitely wouldn't depict this game as educational. For all intents and purposes, it was just some method what the fictional company decided to stick to.&lt;/p&gt;

&lt;p&gt;Most importantly, this game subjects the player to the encumbering of technical debt in a way which could never happen in real life. Time goes fast, and once you reach unbearable levels of complexity, you are submerged in a pool of deadlines impossible to meet. There is no actual losing condition, but I find it perfectly normal for anyone to give up and exit the game at that point, thus emulating a losing condition all the same.&lt;/p&gt;

&lt;p&gt;It is a bit brutal, even if not on purpose. All with the primary goal of accumulating points which don't translate to promotions, raises, or any other kind of advancement or compensation. In retrospective, this game feels like a prank on myself. A self satire of engineering endeavours under the narrow mindset of testing a software project as the means to further work on the same project, where the goal is to optimize for more features, more quality, and less technical debt, all under the guise of a seemingly familiar daily routine.&lt;/p&gt;

&lt;p&gt;Maybe next time I'll just build a platformer or something. 😐&lt;/p&gt;




&lt;p&gt;You can play 10x Sprint Master on a modern web browser &lt;a href="https://e-net4.itch.io/10x-sprint-master" rel="noopener noreferrer"&gt;here&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>gamedev</category>
      <category>webassembly</category>
      <category>rust</category>
      <category>css</category>
    </item>
    <item>
      <title>Migrating from quick-error to SNAFU: a story on revamped error handling in Rust</title>
      <dc:creator>Eduardo Pinho</dc:creator>
      <pubDate>Fri, 28 Aug 2020 18:36:19 +0000</pubDate>
      <link>https://dev.to/e_net4/migrating-from-quick-error-to-snafu-a-story-on-revamped-error-handling-in-rust-58h9</link>
      <guid>https://dev.to/e_net4/migrating-from-quick-error-to-snafu-a-story-on-revamped-error-handling-in-rust-58h9</guid>
      <description>&lt;p&gt;This post describes my more-than-a-month long story of refactoring existing error definitions and error handling code in my largest project written in Rust. &lt;a href="https://github.com/Enet4/dicom-rs" rel="noopener noreferrer"&gt;DICOM-rs&lt;/a&gt; is a pure Rust implementation of the &lt;a href="https://dicomstandard.org" rel="noopener noreferrer"&gt;DICOM standard&lt;/a&gt;, the near-ubiquitous standard for representing and communicating medical imaging data. The refactor hereby described on this project was an immediately apparent improvement, enabling me to track down the cause of a recently discovered bug in one of its crates. Read on to learn more!&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; &lt;a href="https://doc.rust-lang.org/1.45.2/book/ch09-00-error-handling.html" rel="noopener noreferrer"&gt;Basic understanding&lt;/a&gt; of error handling in Rust is expected, as the post will be specifically about the thought process of refactoring all error handling from one library to another. Moreover, for a wider overview of modern error handling, I greatly recommend watching &lt;a href="https://www.youtube.com/watch?v=rAF8mLI0naQ" rel="noopener noreferrer"&gt;Jane Lusby's talk&lt;/a&gt; from RustConf 2020.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why change this?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;quick-error&lt;/code&gt; served its purpose when it was used in the DICOM-rs ecosystem, which was to facilitate the declaration of multi-variant error types, without the hassle of adding the necessary &lt;code&gt;impl&lt;/code&gt; boilerplate of an error type.&lt;br&gt;
It also made it easy (and as we'll see, &lt;em&gt;too easy&lt;/em&gt;) to convert a source error into a dedicated variant of our own error type, so that such an error could be automatically converted when necessary.&lt;/p&gt;

&lt;p&gt;A quick example (no pun intended):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;quick_error!&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;BadIo&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;io&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nf"&gt;display&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"I/O error: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;do_things&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// now if any function returns a standard I/O error,&lt;/span&gt;
    &lt;span class="c1"&gt;// `?` will just convert it to our `Error` type&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;read_my_file&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;                   &lt;span class="c1"&gt;// here&lt;/span&gt;
    &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;fs&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;write&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"out.bin"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nf"&gt;processed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;data&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;  &lt;span class="c1"&gt;// and here too&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It turns out that this approach to error handling has a damaging side-effect: error values &lt;code&gt;E&lt;/code&gt; in a &lt;code&gt;Result&amp;lt;T, E&amp;gt;&lt;/code&gt; only hold the data that we put in them. They do not keep track of any places where the error was raised with the &lt;code&gt;?&lt;/code&gt; operator, unless we introduce that as part of the conversion into new error types. In this case however, the conversion made in this code introduces &lt;em&gt;no additional information&lt;/em&gt; about the context where the operation failed, as I/O errors are always put into the same top-level error variant &lt;code&gt;BadIo&lt;/code&gt;. The problem becomes evident when we run our &lt;code&gt;do_things&lt;/code&gt; and the only error information we get is the following:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;I/O Error: Permission denied
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This could well be the display output of an &lt;code&gt;Error::BadIo&lt;/code&gt; where the inner error has the kind &lt;code&gt;PermissionDenied&lt;/code&gt;. Since this could happen either when reading from a file or when writing to "out.bin", we remain clueless about where the error truly happened.&lt;/p&gt;

&lt;p&gt;This error provides poor informative value, which only gets worse as the number of layers in the program increases. The current top layer in DICOM-rs is &lt;code&gt;dicom-object&lt;/code&gt;, then followed by two layers of abstraction in &lt;code&gt;dicom-parser&lt;/code&gt;, then another one in &lt;code&gt;dicom-encoding&lt;/code&gt;. &lt;code&gt;dicom-core&lt;/code&gt; is at the bottom, and also has error types of its own. The way these errors were built led to very poor quality error outputs when errors occurred (e.g. &lt;code&gt;PrematureEof&lt;/code&gt; could be the full error output when trying to read a file). Worse, since they did not contain a backtrace, the program would not give us the lines of code that led into the error, much unlike in a panic, where by default it would be provided, usually by setting the &lt;code&gt;RUST_BACKTRACE&lt;/code&gt; environment variable.&lt;/p&gt;

&lt;p&gt;It was clear enough for me that the error types in DICOM-rs&lt;br&gt;
should be &lt;strong&gt;more informative&lt;/strong&gt;, and should contain a backtrace if desired.&lt;/p&gt;
&lt;h2&gt;
  
  
  What else then?
&lt;/h2&gt;

&lt;p&gt;The world of error handling in Rust has seen substantial &lt;br&gt;
changes. In its early stages, &lt;code&gt;quick-error&lt;/code&gt; and &lt;code&gt;error-chain&lt;/code&gt; were commonly employed to create errors with ease. &lt;code&gt;error-chain&lt;/code&gt; in particular was known for its emphasis on chaining error causes, pretty much as the name implies. &lt;code&gt;failure&lt;/code&gt; emerged with a new error interface that would allow users to cast down the cause of an error, something which was missing in the standard definition. Eventually, &lt;code&gt;std::error::Error&lt;/code&gt; was in fact updated to accommodate this major benefit from &lt;code&gt;failure&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In recent times, error handling libraries aim to provide utilities for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;declaring structural error types with automatic trait implementations, 
(usually via procedural macros, which were unstable back in 2016);&lt;/li&gt;
&lt;li&gt;easier fact checking and/or error throwing through the program stack;&lt;/li&gt;
&lt;li&gt;presenting errors to the user in a human readable fashion;&lt;/li&gt;
&lt;li&gt;adding compatibility layers to the generation of backtraces, even with a stable Rust toolchain;&lt;/li&gt;
&lt;li&gt;an alternative error handling syntax.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Some libraries are intended to be used exclusively, whereas others can complement with existing code (e.g. &lt;code&gt;fehler&lt;/code&gt;). The ones most commonly heard about these days are &lt;code&gt;thiserror&lt;/code&gt;, &lt;code&gt;anyhow&lt;/code&gt;, &lt;code&gt;eyre&lt;/code&gt;, and &lt;code&gt;snafu&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why SNAFU?
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;thiserror&lt;/code&gt; provides similar benefits to &lt;code&gt;quick-error&lt;/code&gt;, with the plus that it uses nicely integrated procedural macros instead of rule-based macros. Those who have played with them a lot will often find diagnostics to be substantially better in the former. It also means that we do not need to increase the compiler's recursion limit whenever our error type increases in size, a consequence of the &lt;code&gt;quick_error!&lt;/code&gt; macro being defined in a recursive fashion. On the other hand, there isn't much else about it, so I could easily be taking the risk of simply rewriting errors without gaining the intended enhancements.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;anyhow&lt;/code&gt; and &lt;code&gt;eyre&lt;/code&gt; are mostly designed for applications, as they facilitate the aggregation of multiple error types into a single source of information about the error. This error can be expanded across the program stack via the extended &lt;code&gt;context&lt;/code&gt; operator, hence gaining additional information. They deliberately employ a "kitchen-sink" error type, which is more complicated to take part in code flow if necessary. When it comes to libraries, structured error types are usually preferred. Using one of these in CLI tools under the domain of DICOM-rs (such as &lt;code&gt;dcmdump&lt;/code&gt;) is not out of the question, but is currently left as a backlog task.&lt;/p&gt;

&lt;p&gt;And then there's SNAFU, which comes with:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an opinionated procedural macro for declaring error types and variants;&lt;/li&gt;
&lt;li&gt;compile-time generated constructs for context building
and automatic backtrace generation;&lt;/li&gt;
&lt;li&gt;support for both standard (unstable) and stable (crate-based) backtraces;&lt;/li&gt;
&lt;li&gt;and a well established &lt;a href="https://docs.rs/snafu/0.6.8/snafu/guide/philosophy/index.html" rel="noopener noreferrer"&gt;philosophy&lt;/a&gt; on how error types should be designed.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As a project which may grow in unexpected ways, I wanted a library which could also adapt to that scale. Although opinionated, SNAFU incentivizes all the way towards well structured and useful errors.&lt;/p&gt;
&lt;h2&gt;
  
  
  Things to keep in mind
&lt;/h2&gt;

&lt;p&gt;Next is a set of topics which will hopefully transmit the experience that I gathered while doing this rewrite,&lt;br&gt;
and may help bring new ideas into your own error handling strategy.&lt;/p&gt;
&lt;h3&gt;
  
  
  Follow the guidelines
&lt;/h3&gt;

&lt;p&gt;This one is the most obvious. To make error types with SNAFU, one will have a better experience if you follow the advice given in the documentation.&lt;/p&gt;

&lt;p&gt;One of the most relevant topics is to prefer module-level error types over crate-level error types. If your code is well organised in its module hierarchy, then it is also likely that those modules will face a different set of errors. For example, the &lt;code&gt;dicom-encoding&lt;/code&gt; crate is focused on decoding and encoding primitive DICOM data structures from/into a medium (often a file or the network).&lt;br&gt;
In either case, a &lt;code&gt;std::io::Error&lt;/code&gt; could be the root cause of the error, but the circumstances of reading and writing are distinct and may often have their own kinds of errors (e.g. invariant checking when reading data headers). By creating an error type for decoding and another one for encoding, errors will be more focused and only need to be as complex as they need to be.&lt;/p&gt;
&lt;h3&gt;
  
  
  Go top-down, not bottom-up
&lt;/h3&gt;

&lt;p&gt;When rewriting existing code, it is much easier to start with errors which emerge at a higher abstraction level. Every change in a crate's error types &lt;em&gt;needs to be propagated&lt;/em&gt; into its dependents. If the same dependents are not structured in a way as to be prepared for this change, the amount of code that needs to be rewritten at the top will increase.&lt;/p&gt;

&lt;p&gt;At some point during this refactor, I had something like the error type below at the DICOM object file reading module level. This is a high level abstraction over DICOM data sets, so as to be perceived as a dictionary of attributes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[derive(Debug,&lt;/span&gt; &lt;span class="nd"&gt;Snafu)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="nd"&gt;#[snafu(display(&lt;/span&gt;&lt;span class="s"&gt;"Could not read data set token"&lt;/span&gt;&lt;span class="nd"&gt;))]&lt;/span&gt;
    &lt;span class="n"&gt;ReadToken&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;dicom_parser&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;dicom_parser::error::Error&lt;/code&gt; is the crate-level error for the mid-level parser and printer of DICOM data, yet to be refactored. &lt;code&gt;dicom-parser&lt;/code&gt; actually gathers two levels of abstraction here, something which definitely does not favour the decision to use a single error type for all modules in the crate.&lt;/p&gt;

&lt;p&gt;Once we follow the advice of keeping one or more errors per module, we'll have more specific errors and the code above will fail. However, if the variants were well defined, the error to choose for the variant &lt;code&gt;ReadToken&lt;/code&gt; will be quite intuitive, requiring no further changes other than the source type.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[derive(Debug,&lt;/span&gt; &lt;span class="nd"&gt;Snafu)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="nd"&gt;#[snafu(display(&lt;/span&gt;&lt;span class="s"&gt;"Could not read data set token"&lt;/span&gt;&lt;span class="nd"&gt;))]&lt;/span&gt;
    &lt;span class="n"&gt;ReadToken&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;#[snafu(backtrace)]&lt;/span&gt;
        &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;dicom_parser&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;read&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I had tried to convert &lt;code&gt;dicom-core&lt;/code&gt; to use SNAFU at least twice until I admitted that this wasn't the right way. It would blow up most of the other crates in the project and require me to either change the entire thing at once or make changes that would eventually be rewritten anyway.&lt;/p&gt;

&lt;h3&gt;
  
  
  Work with the error type selectors, not against them
&lt;/h3&gt;

&lt;p&gt;Some parts of the documentation may be easy to miss out, because some of the methods do not exist in a single struct or trait. Rather, error type selectors are procedurally built, and hidden behind the &lt;code&gt;Snafu&lt;/code&gt; macro.&lt;/p&gt;

&lt;p&gt;As for a concrete example, if we have this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[derive(Debug,&lt;/span&gt; &lt;span class="nd"&gt;Snafu)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[snafu(display(&lt;/span&gt;
        &lt;span class="s"&gt;"Undefined value length of element tagged {} at position {}"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt;
        &lt;span class="nd"&gt;tag,&lt;/span&gt;
        &lt;span class="nd"&gt;position&lt;/span&gt;
    &lt;span class="nd"&gt;))]&lt;/span&gt;
    &lt;span class="n"&gt;UndefinedValueLength&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;u64&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;backtrace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Backtrace&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One might think that we need to write this to build the error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="n"&gt;header&lt;/span&gt;
    &lt;span class="nf"&gt;.length&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;// custom length type&lt;/span&gt;
    &lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;     &lt;span class="c1"&gt;// Option&amp;lt;_&amp;gt;&lt;/span&gt;
    &lt;span class="nf"&gt;.ok_or_else&lt;/span&gt;&lt;span class="p"&gt;(||&lt;/span&gt; &lt;span class="nn"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;UndefinedValueLength&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.bytes_read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="py"&gt;.tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;backtrace&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;Backtrace&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When in fact the same process is substantially easier and less error prone with selectors:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="n"&gt;header&lt;/span&gt;
    &lt;span class="nf"&gt;.length&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="nf"&gt;.context&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;UndefinedValueLength&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.bytes_read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;tag&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;header&lt;/span&gt;&lt;span class="py"&gt;.tag&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;context&lt;/code&gt; method encapsulates information about a specific error variant, while optionally encapsulating an error and generating backtraces. And all it takes to have this &lt;code&gt;context&lt;/code&gt; method is to have our Snafu-built error type and import &lt;code&gt;snafu::ResultExt&lt;/code&gt; and/or &lt;code&gt;snafu::OptionExt&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Even when &lt;code&gt;context&lt;/code&gt; isn't suitable (which is when you don't have a result nor an option), you can call &lt;code&gt;.build()&lt;/code&gt; or&lt;code&gt;.fail()&lt;/code&gt; on top of a selector.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;date_str&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nf"&gt;is_valid_date&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;InvalidDateValue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.bytes_read&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nf"&gt;.fail&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In fact, you should never really need to create errors manually.&lt;/p&gt;

&lt;h3&gt;
  
  
  Libraries, stabilize your APIs
&lt;/h3&gt;

&lt;p&gt;As an ecosystem mostly comprised of libraries, it is admitted that changing any of these public error enum types leads to a breaking change, which would introduce a barrier to an eventual upgrade.&lt;/p&gt;

&lt;p&gt;One easy step that gives more freedom to public error types is adding &lt;code&gt;#[non_exhaustive]&lt;/code&gt;. This makes it so that matching on the value requires a "catch-all" match arm, which means that introducing new variants will not break existing code.&lt;/p&gt;

&lt;p&gt;There usually isn't a need to exhaustively consider each error variant, so this change brings immediate benefits with little undesirable consequences. The same attribute can be applied to the struct variants themselves, so that adding more contextual fields to an error variant also does not lead to a major breaking change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[derive(Debug,&lt;/span&gt; &lt;span class="nd"&gt;Snafu)]&lt;/span&gt;
&lt;span class="nd"&gt;#[non_exhaustive]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="nd"&gt;#[snafu(display(&lt;/span&gt;&lt;span class="s"&gt;"Could not read data set token: {}"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;source))]&lt;/span&gt;
    &lt;span class="nd"&gt;#[non_exhaustive]&lt;/span&gt;
    &lt;span class="n"&gt;ReadToken&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;#[snafu(backtrace)]&lt;/span&gt;
        &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;dicom_parser&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;read&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can also choose to make an error &lt;em&gt;opaque&lt;/em&gt;. Opaque errors are reported with the same information, except that their implementation is behind private constructs, therefore purposely limiting the interactions possible with that error. As users of the API cannot touch the implementation details of the error, any change will not break existing code, although it may change the errors displayed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[derive(Debug,&lt;/span&gt; &lt;span class="nd"&gt;Snafu)]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="nf"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;InnerError&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nd"&gt;#[derive(Debug,&lt;/span&gt; &lt;span class="nd"&gt;Snafu)]&lt;/span&gt;
&lt;span class="nd"&gt;#[non_exhaustive]&lt;/span&gt;
&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;InnerError&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;  &lt;span class="c1"&gt;// no longer public&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Backtrace? What backtrace?
&lt;/h3&gt;

&lt;p&gt;At some point before this refactor, I had this shallow idea that all I was missing in an error report was the backtrace to the part of the code that constructed the error. However, SNAFU advocates for &lt;em&gt;semantic backtracing&lt;/em&gt;: to have good enough error types and variants, conveying enough information so that you don't need a backtrace in practice.&lt;/p&gt;

&lt;p&gt;The current recommendation would be to introduce a &lt;code&gt;backtrace&lt;/code&gt; on leaf error variants, either always generated or conditionally generated (by wrapping an &lt;code&gt;Option&lt;/code&gt; around it), and see for yourself how useful they are. My early experience so far is that a backtrace makes reaching the problematic code a bit easier, but semantic backtracing already provides an outstanding clue of what the issue might be.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ready to report?
&lt;/h3&gt;

&lt;p&gt;Consider thinking about how you are going to report the errors to the user, and adjust your display implementations accordingly.&lt;/p&gt;

&lt;p&gt;Most examples in the SNAFU guide show the following pattern:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;#[derive(Debug,&lt;/span&gt; &lt;span class="nd"&gt;Snafu)]&lt;/span&gt;
&lt;span class="nd"&gt;#[non_exhaustive]&lt;/span&gt;
&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Error&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[snafu(display(&lt;/span&gt;&lt;span class="s"&gt;"Could not read data set token: {}"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;source))]&lt;/span&gt;
    &lt;span class="nd"&gt;#[non_exhaustive]&lt;/span&gt;
    &lt;span class="n"&gt;ReadToken&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;#[snafu(backtrace)]&lt;/span&gt;
        &lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;dicom_parser&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;dataset&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;read&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice how the &lt;code&gt;source&lt;/code&gt; field is chained to the tail of&lt;br&gt;
the type's display implementation. This makes it so that a single line print is enough to present all information about the error (minus the backtrace).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[ERROR] {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;One possible output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ERROR] Could not read data set token: Could not read item value: Undefined value length of element tagged (5533,5533) at position 3548
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, this will not integrate well with other error reporting facilities, such as &lt;code&gt;anyhow&lt;/code&gt; or &lt;code&gt;eyre&lt;/code&gt;, which are already prepared to traverse the chain of error causes and show them individually.&lt;/p&gt;

&lt;p&gt;If we were to turn our error into an &lt;code&gt;eyre::Report&lt;/code&gt; and print that:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[ERROR] {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nn"&gt;eyre&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;Report&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;from&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We get these maddening repetitions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ERROR] Could not read data set token: Could not read item value: Undefined value length of element tagged (5533,5533) at position 3548

Caused by:
   0: Could not read item value: Undefined value length of element tagged (5533,5533) at position 3548
   1: Undefined value length of element tagged (5533,5533) at position 3548
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this case, we would want a display message that does not print its source error.&lt;/p&gt;

&lt;p&gt;This issue is not necessary specific to eyre, but will emerge any time that you use a different kind of error reporter. An equivalent report function in SNAFU would be quite similar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;report&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;'static&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;where&lt;/span&gt;
    &lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;error&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;snafu&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ErrorCompat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;E&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Send&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;Sync&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"[ERROR] {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="nf"&gt;.source&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Caused by:"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="nn"&gt;std&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nn"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;successors&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;|&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="nf"&gt;.source&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="nf"&gt;.enumerate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"   {}: {}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;i&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nf"&gt;Some&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;backtrace&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;ErrorCompat&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;backtrace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;err&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Backtrace:"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nd"&gt;eprintln!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"{}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;backtrace&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(Note: This reporter will &lt;em&gt;always&lt;/em&gt; try to print an existing backtrace, regardless of environment variables set.)&lt;/p&gt;

&lt;p&gt;So yes, you need to define upfront how your errors are supposed to be reported:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implement error displays that show the full chain of errors, so that consumers only have to call a standard print function;&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;Or&lt;/em&gt; leave the task of showing the list of causes to a reporter, hence giving more flexibility to the consumer of the API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There currently isn't an easy way to switch from one to the other. I chose the latter for DICOM-rs because I am interested in maintaining control of how errors are reported&lt;br&gt;
in the project, and I can easily adjust all application crates accordingly. There are also chances of this pattern to become more common place in the wider Rust ecosystem.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;In the end, I cannot claim that this was simply the best rewrite one could do, but I can say that rewriting it was a good decision. With a &lt;a href="https://github.com/Enet4/dicom-rs/pull/62" rel="noopener noreferrer"&gt;pull request&lt;/a&gt; comprising +4,694/−2,900 lines in 23 commits, the quick-error dependency was removed and snafu took over, much to the delight of future users of the DICOM-rs ecosystem.&lt;/p&gt;

&lt;p&gt;As &lt;a href="https://github.com/Enet4/dicom-rs" rel="noopener noreferrer"&gt;DICOM-rs&lt;/a&gt; may one day be a cornerstone of future generations of medical imaging software, this refactor came not a moment too soon. Future work will concentrate on other important matters that might be worth communicating as well. Among the topics in the project's &lt;a href="https://github.com/Enet4/dicom-rs/wiki/Roadmap" rel="noopener noreferrer"&gt;roadmap&lt;/a&gt;, there is logging, lazy file loading, a data-oriented test harness, and a higher-level image abstraction.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>errors</category>
      <category>snafu</category>
    </item>
    <item>
      <title>Rust 2020: Tropes and Jargon</title>
      <dc:creator>Eduardo Pinho</dc:creator>
      <pubDate>Tue, 12 Nov 2019 23:37:02 +0000</pubDate>
      <link>https://dev.to/e_net4/rust-2020-tropes-and-jargon-5f07</link>
      <guid>https://dev.to/e_net4/rust-2020-tropes-and-jargon-5f07</guid>
      <description>&lt;p&gt;Dear Rust community,&lt;/p&gt;

&lt;p&gt;I am writing this to announce a lame, but hopefully fruitful confession through this post. I had originally intended to make this a Twitter thread, but eventually the Rust 2020 call for posts emerged, and I saw it as an opportunity to expand this.&lt;/p&gt;

&lt;p&gt;It's not that much of a surprise when I say that there is a time and place for everything. Even a community in tech, such as the Rust community, may not only be concerned about the next generation of software, but may also be interested in establishing grounds for creativity and room for inside jokes. One day, while writing &lt;code&gt;.map(|_| ())&lt;/code&gt;, I realised how the closure inside &lt;a href="http://i.imgur.com/R49wDFh.jpg" rel="noopener noreferrer"&gt;looked like a toilet&lt;/a&gt; rotated 90 degrees counter-clockwise. So I tweeted this in May 2017, and started using the expression since. &lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-869303385513656320-735" src="https://platform.twitter.com/embed/Tweet.html?id=869303385513656320"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-869303385513656320-735');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=869303385513656320&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;On the other hand, most of those who already knew about the toilet closure might have seen it from &lt;a href="https://www.reddit.com/r/rustjerk/comments/8udbth/a_new_war_begins_choose_your_side/" rel="noopener noreferrer"&gt;Reddit&lt;/a&gt;, around June 2018. Yup, that was also me. Memes will slowly and softly deduct my sanity. &lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--B2w09jFT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.redd.it/gnubxlvgxl611.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--B2w09jFT--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_800/https://i.redd.it/gnubxlvgxl611.png" alt="You must choose: (1) one toilet closure; (2) one built-in boi" width="800" height="499"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And yet this toilet is as pertinent in Rust code as it was 2 years ago, perhaps even more due to the latest advances in asynchronous programming with Rust. And it is very often preferred over &lt;code&gt;drop&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;iframe class="tweet-embed" id="tweet-1156577337204465664-148" src="https://platform.twitter.com/embed/Tweet.html?id=1156577337204465664"&gt;
&lt;/iframe&gt;

  // Detect dark theme
  var iframe = document.getElementById('tweet-1156577337204465664-148');
  if (document.body.className.includes('dark-theme')) {
    iframe.src = "https://platform.twitter.com/embed/Tweet.html?id=1156577337204465664&amp;amp;theme=dark"
  }



&lt;/p&gt;

&lt;p&gt;So yes, I was likely the one who coined "toilet closure". Although I agree we should be grabbing our leads tighter ("less jargon", &lt;a href="https://llogiq.github.io/2019/10/30/rust-2020.html" rel="noopener noreferrer"&gt;as suggested by Llogiq&lt;/a&gt;), this process is almost organic and not entirely avoidable on the behalf of a community presenting superior levels of enthusiasm.&lt;/p&gt;

&lt;p&gt;Nonetheless,the part that worries me most is how hard it is to search about these things. We happen to have great technical resources out there, but not for such a jargon as "toilet closure". This one is particularly tricky to search about, as you end up with &lt;a href="https://www.bbc.com/news/uk-wales-48528887" rel="noopener noreferrer"&gt;public plumbing issues&lt;/a&gt; instead.&lt;/p&gt;

&lt;p&gt;So I came up with this idea of uniting all this jargon, tropes, acronyms, and other expressions into an open-source repository. &lt;a href="https://enet4.github.io/rust-tropes" rel="noopener noreferrer"&gt;Rust Tropes&lt;/a&gt; intends to be a community-curated list of expressions and concepts which may emerge when talking about Rust or when engaging with other &lt;a href="https://enet4.github.io/rust-tropes#rustacean" rel="noopener noreferrer"&gt;Rustaceans&lt;/a&gt;, some of which are not necessarily technical.&lt;/p&gt;

&lt;p&gt;This is a work in progress, and I hope to receive some assistance. Feel free to contribute with more content, better styling, and other things. &lt;a href="https://github.com/Enet4/rust-tropes" rel="noopener noreferrer"&gt;https://github.com/Enet4/rust-tropes&lt;/a&gt;&lt;br&gt;
Your assistance in spreading and expanding this source is much appreciated, and it's one of my wishes for Rust 2020 for this resource to have some usefulness to all the confused people out there wondering what &lt;a href="https://enet4.github.io/rust-tropes#pre-poop-your-pants" rel="noopener noreferrer"&gt;pre-pooping your pants&lt;/a&gt; is, or why &lt;code&gt;unsafe&lt;/code&gt; code attracts &lt;a href="https://enet4.github.io/rust-tropes#eat-your-laundry" rel="noopener noreferrer"&gt;laundry connoisseurs&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Also, we could really use &lt;a href="https://enet4.github.io/rust-tropes#generic-associated-types" rel="noopener noreferrer"&gt;GATs&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>rust2020</category>
      <category>tropes</category>
      <category>jargon</category>
    </item>
  </channel>
</rss>
