<?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>Forem: Vinicius Lambardozzi</title>
    <description>The latest articles on Forem by Vinicius Lambardozzi (@voxelstack).</description>
    <link>https://forem.com/voxelstack</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%2F1130374%2Fa7289940-cd84-4108-8aca-54a71f2497cf.png</url>
      <title>Forem: Vinicius Lambardozzi</title>
      <link>https://forem.com/voxelstack</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/voxelstack"/>
    <language>en</language>
    <item>
      <title>Rewriting Comlink in Rust (kinda)</title>
      <dc:creator>Vinicius Lambardozzi</dc:creator>
      <pubDate>Sat, 26 Aug 2023 15:37:56 +0000</pubDate>
      <link>https://forem.com/voxelstack/rewriting-comlink-in-rust-kinda-5a7n</link>
      <guid>https://forem.com/voxelstack/rewriting-comlink-in-rust-kinda-5a7n</guid>
      <description>&lt;p&gt;In this post I share my thought process for creating a Rust wrapper around &lt;code&gt;postMessage&lt;/code&gt; and making it friendlier with a procedural macro. I then use it to build a tally counter that runs on a web worker. &lt;a href="https://github.com/voxelstack/atlas" rel="noopener noreferrer"&gt;The resulting proof of concept&lt;/a&gt; is available on my GitHub.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Much of what I describe here was done for fun and learning, but there is a practical takeaway for WebAssembly + Svelte: observables serve as Svelte stores. That means if your WebAssembly code can create an observable and return it to JavaScript, &lt;code&gt;{$observable}&lt;/code&gt; will be reactive even for changes on the WebAssembly side. Skip to "Finishing touches" for more details on that. I found out about observables being usable as stores after reading an &lt;a href="https://timdeschryver.dev/blog/unlocking-reactivity-with-svelte-and-rxjs" rel="noopener noreferrer"&gt;article about RxJs with Svelte&lt;/a&gt; by Tim Deschryver.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  The spark
&lt;/h2&gt;

&lt;p&gt;WebAssembly already looked very promising as a way to satisfy my cravings for lower-level 3D graphics, but there are many new and exciting things happening: &lt;a href="https://github.com/rustwasm/wasm-bindgen" rel="noopener noreferrer"&gt;wasm-bindgen&lt;/a&gt; means I can write Rust, which I'm a big fan of; &lt;a href="https://wgpu.rs/" rel="noopener noreferrer"&gt;wgpu&lt;/a&gt; lets me move on from OpenGL and go back to studying modern graphics APIs; &lt;a href="https://github.com/GoogleChromeLabs/wasm-bindgen-rayon" rel="noopener noreferrer"&gt;wasm-bindgen-rayon&lt;/a&gt; gives me threads; and finally &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCanvas" rel="noopener noreferrer"&gt;OffscrenCanvas&lt;/a&gt; allows me to do all the rendering inside a web worker, making all of this look pretty similar to how you'd write a native renderer.&lt;/p&gt;

&lt;p&gt;With &lt;a href="https://kit.svelte.dev/" rel="noopener noreferrer"&gt;SvelteKit&lt;/a&gt; as an application framework, we gain access to &lt;a href="https://mdsvex.com/" rel="noopener noreferrer"&gt;mdsvex&lt;/a&gt;: a way to write interactive markdown.&lt;/p&gt;

&lt;h2&gt;
  
  
  The destination
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd5kpewqyicr1xumrlj4a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd5kpewqyicr1xumrlj4a.png" alt="A diagram with a dotted vertical line down the middle. On the top left is a monitor icon labeled "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The original idea was to have a web worker drawing to a canvas and a DOM-based UI that controls it. In the image above, everything to the left of the horizontal line exists within the main thread, while everything to the right is on web workers.&lt;/p&gt;

&lt;p&gt;It all runs on the browser, but to make it easier to reason about, I call the primary web worker a "render server" while the code running on the main thread is the "client". Each gear represents a web worker spawned by wasm-bindgen-rayon, exposed to Rust as threads. The envelope represents data crossing the &lt;code&gt;postMessage&lt;/code&gt; boundary.&lt;/p&gt;

&lt;h2&gt;
  
  
  The journey
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/GoogleChromeLabs/comlink" rel="noopener noreferrer"&gt;Comlink&lt;/a&gt; simplifies communication with web workers by providing an RPC implementation built on top of &lt;code&gt;postMessage&lt;/code&gt;. I decided to write the client and server in Rust without using Comlink. A way to communicate with the render server was the first problem to solve, and for that I needed a Rust-friendly way to send messages between the main thread and a web worker.&lt;/p&gt;

&lt;h3&gt;
  
  
  Build setup
&lt;/h3&gt;

&lt;p&gt;The client and server code are in separate crates, and each gets built into its own WebAssembly package. This build setup increases the final bundle size, doubling the overhead of both wasm-bindgen and the communication code. Building a single package and being careful to only use client code on the main thread and server code on a worker addresses that. However, wasm-bindgen-rayon generates code that should only run on workers, so to avoid having to work around that I opted to keep them separate on this demo.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A small takeaway from this setup is that it is possible to organize code with a cargo workspace while using wasm-pack: &lt;a href="https://github.com/voxelstack/atlas/blob/f6b16ab1b759cb7a2a74154198ba12089cfbbb7d/package.json#L8-L9" rel="noopener noreferrer"&gt;the build command can take a path&lt;/a&gt;. While this means wasm-pack builds the individual crates and doesn't see the workspace, the language server does, so it's still helpful. Just remember that the members will not &lt;a href="https://doc.rust-lang.org/cargo/reference/workspaces.html?highlight=workspace#the-package-table" rel="noopener noreferrer"&gt;inherit keys set on the workspace&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Something that can communicate
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/search?q=postMessage" rel="noopener noreferrer"&gt;Many things can send messages&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget" rel="noopener noreferrer"&gt;many things implement &lt;code&gt;EventTarget&lt;/code&gt;&lt;/a&gt;. Our use case is exchanging messages between the main thread and a web worker. That involves working with two interfaces: &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Worker" rel="noopener noreferrer"&gt;&lt;code&gt;Worker&lt;/code&gt;&lt;/a&gt; will be used on the client to send messages and listen to incoming responses from the server, and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/DedicatedWorkerGlobalScope" rel="noopener noreferrer"&gt;&lt;code&gt;DedicatedWorkerGlobalScope&lt;/code&gt;&lt;/a&gt; will be used on the server to send messages and listen to incoming responses from the client.&lt;/p&gt;

&lt;p&gt;Messages are a common part of these two interfaces: they both have a &lt;code&gt;postMessage&lt;/code&gt;, and both implement &lt;code&gt;EventTarget&lt;/code&gt; (specifically, they can receive "message" events). The first step was to define &lt;a href="https://github.com/voxelstack/atlas/blob/f6b16ab1b759cb7a2a74154198ba12089cfbbb7d/src/atlas/comms/src/port.rs#L6-L12" rel="noopener noreferrer"&gt;a trait for things that can send and receive messages&lt;/a&gt;. It describes the ability to send messages, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Transferable_objects" rel="noopener noreferrer"&gt;transfer objects&lt;/a&gt;, and add or remove event listeners:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;RawPort&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;send_raw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;JsValue&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;transfer_raw&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;JsValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;JsValue&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;add_raw_listener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nn"&gt;js_sys&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;remove_raw_listener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nn"&gt;js_sys&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;The &lt;code&gt;start&lt;/code&gt; method is needed when &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/MessagePort/start" rel="noopener noreferrer"&gt;working with message ports&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;code&gt;raw&lt;/code&gt; indicates that these methods work with &lt;code&gt;JsValue&lt;/code&gt;s (the Rust representation of an object owned by JavaScript). We'll be using the implementations of &lt;code&gt;RawPort&lt;/code&gt; for &lt;a href="https://github.com/voxelstack/atlas/blob/f6b16ab1b759cb7a2a74154198ba12089cfbbb7d/src/atlas/comms/src/port.rs#L14-L34" rel="noopener noreferrer"&gt;&lt;code&gt;Worker&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://github.com/voxelstack/atlas/blob/f6b16ab1b759cb7a2a74154198ba12089cfbbb7d/src/atlas/comms/src/port.rs#L36-L56" rel="noopener noreferrer"&gt;&lt;code&gt;DedicatedWorkerGlobalScope&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Converting messages to &lt;code&gt;JsValue&lt;/code&gt;s and back shouldn't be done manually, but with &lt;code&gt;RawPort&lt;/code&gt; it is possible to create an entity that sends Rust types:&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;struct&lt;/span&gt; &lt;span class="nf"&gt;Port&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;Box&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="n"&gt;RawPort&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;impl&lt;/span&gt; &lt;span class="n"&gt;Port&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;todo!&lt;/span&gt;&lt;span class="p"&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;add_listener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Closure&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nf"&gt;Fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MessageEvent&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;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Listener&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;todo!&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 &lt;code&gt;Listener&lt;/code&gt; handle is helpful for any situation where you want to pass a Rust closure to JavaScript. Since JavaScript has no concept of ownership, the &lt;code&gt;listener&lt;/code&gt; closure would be dropped when &lt;code&gt;add_listener&lt;/code&gt; returns, and the event handler would access an invalid reference. &lt;a href="https://rustwasm.github.io/docs/wasm-bindgen/examples/closures.html#web-sys-closures" rel="noopener noreferrer"&gt;The example on the wasm-bindgen book&lt;/a&gt; "solves" this by calling &lt;code&gt;closure.forget()&lt;/code&gt;. That leaks memory, so it's worth taking a brief detour to talk about it.&lt;/p&gt;

&lt;h4&gt;
  
  
  Passing Rust closures to JavaScript
&lt;/h4&gt;

&lt;p&gt;Since &lt;code&gt;RawPort&lt;/code&gt; calls a listener closure whenever it receives an event, active listener closures must be valid for as long as the event listener is attached. We can achieve that by storing the closure on a container whose lifetime doesn't exceed the lifetime of the port.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;Listener&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&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;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;'a&lt;/span&gt; &lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="n"&gt;RawPort&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;inner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Closure&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nf"&gt;Fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MessageEvent&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Here we need a reference to the owner to clear the event listener. If we didn't, &lt;code&gt;_marker: PhantomData&amp;lt;&amp;amp; 'a ()&amp;gt;&lt;/code&gt; would be enough to tie the lifetime.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Whoever holds this handle should be able to clear the associated event listener (in case they are no longer interested in that event), but more importantly, the event listener must be cleared if the closure is dropped:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Listener&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&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;clear&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.remove_listener&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;remove_listener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;
            &lt;span class="py"&gt;.owner&lt;/span&gt;
            &lt;span class="nf"&gt;.remove_raw_listener&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;.inner&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unchecked_ref&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Drop&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;Listener&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nv"&gt;'a&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;drop&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="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.remove_listener&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;blockquote&gt;
&lt;p&gt;The public method &lt;code&gt;clear&lt;/code&gt; consumes &lt;code&gt;self&lt;/code&gt; because this handle should not be reused after being cleared.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now &lt;code&gt;add_listener&lt;/code&gt; can return a &lt;code&gt;Listener&lt;/code&gt; handle, and the event handler will never call a closure that has been dropped.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Port&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;add_listener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Closure&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;dyn&lt;/span&gt; &lt;span class="nf"&gt;Fn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;MessageEvent&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;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Listener&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;&lt;span class="nf"&gt;.add_raw_listener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.unchecked_ref&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

        &lt;span class="n"&gt;Listener&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;owner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="na"&gt;.0&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="n"&gt;inner&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;listener&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;h3&gt;
  
  
  What are we sending?
&lt;/h3&gt;

&lt;p&gt;Implementing the &lt;code&gt;send&lt;/code&gt; method is harder. On the first code snippet, the message type was &lt;code&gt;M&lt;/code&gt;. That is convenient because it allows sending and receiving whatever type works best for the situation. Imagine something like this:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Request (client)&lt;/th&gt;
&lt;th&gt;Response (server)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Set { key: "seed", value: "iLikeRocks"}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Ok(())&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Set { key: "skyColor", value: "blue"}&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Err("skyColor expects a hex string")&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Query { key: "memoryUsage" }&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;Ok(128)&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;I left out something important though. Behind the scenes, &lt;code&gt;send&lt;/code&gt; uses &lt;code&gt;postMessage&lt;/code&gt; and that matters:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;messages must be capable of crossing the Rust/JavaScript boundary;&lt;/li&gt;
&lt;li&gt;the data must keep its meaning after being serialized with &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Structured_clone_algorithm" rel="noopener noreferrer"&gt;the structured clone algorithm&lt;/a&gt;;&lt;/li&gt;
&lt;li&gt;some messages might require &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Transferable_objects" rel="noopener noreferrer"&gt;transfering objects&lt;/a&gt; to the destination side (for example, when sending an &lt;code&gt;OffscreenCanvas&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Since &lt;code&gt;post_message&lt;/code&gt; sends &lt;code&gt;JsValue&lt;/code&gt;s and those can cross the Rust/JavaScript boundary, it must be possible to convert messages to &lt;code&gt;JsValue&lt;/code&gt;. An optional list of objects to transfer can be described as an &lt;code&gt;Option&amp;lt;JsValue&amp;gt;&lt;/code&gt;. With those two requirements in mind, we can define a message as a type that can be converted to a tuple &lt;code&gt;(JsValue, Option&amp;lt;JsValue&amp;gt;)&lt;/code&gt;: the content of the message and an an optional list of objects to transfer.&lt;br&gt;
There are three content types to consider here, and I have named them as follows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;"raw"&lt;/code&gt;: wasm-bindgen provides &lt;code&gt;Into&amp;lt;JsValue&amp;gt;&lt;/code&gt;, &lt;code&gt;From&amp;lt;JsValue&amp;gt;&lt;/code&gt; implementations for these types;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"serde"&lt;/code&gt;: &lt;a href="https://rustwasm.github.io/wasm-bindgen/reference/arbitrary-data-with-serde.html#serializing-and-deserializing-arbitrary-data-into-and-from-jsvalue-with-serde" rel="noopener noreferrer"&gt;arbitrary types that must be serialized&lt;/a&gt;, we let &lt;code&gt;serde&lt;/code&gt; handle those;&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;"shareable"&lt;/code&gt;: nested messages composed by content types that fall under one of these three categories.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Finally, the receiving side must be able to convert a &lt;code&gt;JsValue&lt;/code&gt; back into the original type.&lt;/p&gt;

&lt;p&gt;We can write that as a trait. Read the following code as: "&lt;code&gt;Shareable&lt;/code&gt; is something that can be sent using &lt;code&gt;postMessage&lt;/code&gt; while possibly transferring some objects, and then read back as a Rust type on the receiving side".&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;trait&lt;/span&gt; &lt;span class="n"&gt;Shareable&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="nb"&gt;TryInto&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;JsValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JsValue&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;Error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ShareableError&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nb"&gt;TryFrom&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JsValue&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;=&lt;/span&gt; &lt;span class="n"&gt;ShareableError&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;Now, we can finally reason about what the implementation of our send method might look like. First we introduce a trait bound on &lt;code&gt;M&lt;/code&gt;, requiring it to be &lt;code&gt;Shareable&lt;/code&gt;. That allows converting messages into a &lt;code&gt;(data, transfer)&lt;/code&gt; tuple. If there is nothing to transfer, send the data using &lt;code&gt;send_raw&lt;/code&gt;. Otherwise, send the data and transfer some objects with &lt;code&gt;transfer_raw&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="n"&gt;send&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;M&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;M&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;M&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;Shareable&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="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="n"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="nf"&gt;.try_into&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="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;transfer&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;transfer&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.transfer_raw&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="n"&gt;transfer&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="nb"&gt;None&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.send_raw&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="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;There should be error handling here (for example, if serialization fails).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;That is enough. Any type &lt;code&gt;M&lt;/code&gt; that implements &lt;code&gt;Shareable&lt;/code&gt; can be sent between the main thread and a web worker using a &lt;code&gt;Port&lt;/code&gt;. It is transparent too: Rust on both sides, no &lt;code&gt;JsValue&lt;/code&gt;s involved.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;M&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// --snip--&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="nb"&gt;Into&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;JsValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;Option&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JsValue&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="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;M&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// --snip--&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;TryFrom&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;JsValue&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;M&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// --snip--&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Shareable&lt;/span&gt; &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;ClientMessage&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// client.rs&lt;/span&gt;
&lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;m&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;M&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="n"&gt;port&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;m&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// server.rs&lt;/span&gt;
&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="nf"&gt;.add_listener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Closure&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="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MessageEvent&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;m&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;M&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="nf"&gt;.data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.try_into&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="nd"&gt;trace!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;m&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;Going back in the git history, you can see that &lt;a href="https://github.com/voxelstack/atlas/blob/fff1b302016e68ec27b15e84c74bde6664fc6eb0/src/atlas/comms/src/client.rs" rel="noopener noreferrer"&gt;this is what I used for my first tests&lt;/a&gt;. Notice the TODO: &lt;code&gt;// TODO #[derive(Shareable)]&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;#[derive(Shareable)]&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;This only looks convenient because I am hiding the implementations for &lt;code&gt;TryInto&amp;lt;(JsValue, Option&amp;lt;JsValue&amp;gt;)&amp;gt;&lt;/code&gt; and &lt;code&gt;TryFrom&amp;lt;JsValue&amp;gt;&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/voxelstack/atlas/blob/fff1b302016e68ec27b15e84c74bde6664fc6eb0/src/atlas/comms/src/client.rs#L13-L32" rel="noopener noreferrer"&gt;TryInto&lt;/a&gt; must be called before sending and does the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;make a &lt;code&gt;JsArray&lt;/code&gt; and push a string that identifies the type;&lt;/li&gt;
&lt;li&gt;if the type is an enum, push a string that identifies the variant;&lt;/li&gt;
&lt;li&gt;if there are any fields, convert their values to &lt;code&gt;JsValue&lt;/code&gt; and push them to the array;&lt;/li&gt;
&lt;li&gt;if there are things to transfer, make another JsArray and push the transferrable objects.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://github.com/voxelstack/atlas/blob/fff1b302016e68ec27b15e84c74bde6664fc6eb0/src/atlas/comms/src/client.rs#L38-L53" rel="noopener noreferrer"&gt;TryFrom&lt;/a&gt; is called after receiving and does the following:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;read the type identifier from the array;&lt;/li&gt;
&lt;li&gt;if the type is an enum, read the variant identifier;&lt;/li&gt;
&lt;li&gt;if there are any fields, read the &lt;code&gt;JsValue&lt;/code&gt;s and convert them back to Rust types;&lt;/li&gt;
&lt;li&gt;reconstruct the original message.&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;An array representing &lt;code&gt;ClientMessage::Set("key", "value")&lt;/code&gt; looks like this: &lt;code&gt;["ClientMessage", "Set", "key", "value"]&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;These steps are straightforward, but implementing them for every transferrable type is repetitive, error-prone, and inconvenient. Thankfully, that is a perfect use case for &lt;a href="https://doc.rust-lang.org/reference/procedural-macros.html#derive-macros" rel="noopener noreferrer"&gt;derive macros&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Adding the derive attribute to a struct, enum, or union allows you to read the token stream that defines that item and create a new token stream that the compiler then appends to the source. &lt;a href="https://github.com/dtolnay/cargo-expand#example" rel="noopener noreferrer"&gt;cargo-expand lets you see this in action&lt;/a&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  So long, and thanks for all the macros
&lt;/h4&gt;

&lt;p&gt;After adding the macro, we are actually done and can derive the implementation for &lt;code&gt;Shareable&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;All you need to do to turn a Rust type into a valid message type is annotate it with &lt;code&gt;#[derive(Shareable)]&lt;/code&gt; and possibly set some attributes on each item. It is possible to set the representation to be &lt;code&gt;raw&lt;/code&gt;, &lt;code&gt;serde&lt;/code&gt;, or &lt;code&gt;shareable&lt;/code&gt;, which controls how to transform the value into a &lt;code&gt;JsValue&lt;/code&gt;, and it is possible to mark items to transfer. Here is a practical example:&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;Shareable)]&lt;/span&gt;
&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="n"&gt;FontOptions&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[shareable(repr&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"serde"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;family&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nd"&gt;#[shareable(repr&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"serde"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
    &lt;span class="n"&gt;size&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="nd"&gt;#[derive(Debug,&lt;/span&gt; &lt;span class="nd"&gt;Shareable)]&lt;/span&gt;
&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Ping&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nf"&gt;RenderDebugString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nd"&gt;#[shareable(repr&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"serde"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nd"&gt;#[shareable(repr&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"shareable"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt; &lt;span class="n"&gt;FontOptions&lt;/span&gt;
    &lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="n"&gt;CreateWindow&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;#[shareable(repr&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"serde"&lt;/span&gt;&lt;span class="nd"&gt;)]&lt;/span&gt;
        &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nd"&gt;#[shareable(repr&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"raw"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;transfer)]&lt;/span&gt;
        &lt;span class="n"&gt;surface&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;OffscreenCanvas&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;blockquote&gt;
&lt;p&gt;Check &lt;a href="https://github.com/voxelstack/atlas/blob/f6b16ab1b759cb7a2a74154198ba12089cfbbb7d/src/atlas/comms/src/lib.rs#L45-L582" rel="noopener noreferrer"&gt;the macro tests&lt;/a&gt; to see incremental examples of the macro in action.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It derives &lt;code&gt;Shareable&lt;/code&gt; for both enums and structs, handles arbitrary data with serde, works with nested shareable types, and possibly transfers values when posting messages. The macro generates all the required trait implementations, and the &lt;code&gt;Port&lt;/code&gt; provides all the glue code to wrap types capable of sending and receiving messages.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Rust macros are too broad of a topic to cover in detail here, so from &lt;a href="https://github.com/voxelstack/atlas/blob/main/src/atlas/comms_derive/src/lib.rs" rel="noopener noreferrer"&gt;the entire macro derivation&lt;/a&gt; I chose a single highlight for this section: error handling.&lt;/p&gt;

&lt;p&gt;The &lt;a href="https://github.com/dtolnay/proc-macro-workshop" rel="noopener noreferrer"&gt;procedural macro workshop&lt;/a&gt; was the best resource I could find on procedural macros. Jon Gjengset has &lt;a href="https://youtu.be/geovSK3wMB8" rel="noopener noreferrer"&gt;walkthroughs for some of the workshop projects&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Error handling
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0062ih9z15ntsdov70ek.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0062ih9z15ntsdov70ek.png" alt="A screenshot of a Tweet by mcc @mcclure111. The Tweet was posted at 4:30PM - 1 Jun 2018 and has 292 retweets, 962 likes and 20 replies. It reads: In C++ we don't say "&gt;&lt;/a&gt;&amp;gt;::push_back(const block &amp;amp;)': cannot convert argument 1 from 'std::_Vector_iterator&amp;gt;&amp;gt;' to 'block &amp;amp;&amp;amp;'" and I think that's beautiful."/&amp;gt;&lt;/p&gt;

&lt;p&gt;Compilation errors caused by generated code are notoriously annoying to debug and often result in unhelpful error messages. Let's make a typo while defining a repr:&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;Shareable)]&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;WindowProxy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;#[shareable(repr&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"rraw"&lt;/span&gt;&lt;span class="nd"&gt;,&lt;/span&gt; &lt;span class="nd"&gt;transfer)]&lt;/span&gt;
    &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;OffscreenCanvas&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The macro derivation being done in Rust already does a lot to address this issue. My code for parsing the repr attribute looks similar to 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;match&lt;/span&gt; &lt;span class="n"&gt;lit&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;raw&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Repr&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Raw&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;serde&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Repr&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Serde&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;shareable&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;Repr&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Shareable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nd"&gt;todo!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"invalid repr"&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;Since the match must be exaustive, every typo will get caught by the catch-all arm, which is where we should handle this error. When dealing with conditional compilation or generated code, the &lt;a href="https://doc.rust-lang.org/std/macro.compile_error.html" rel="noopener noreferrer"&gt;&lt;code&gt;compile_error!&lt;/code&gt;&lt;/a&gt; macro can signal the compiler to fail. Just as an irrecoverable error during runtime should invoke &lt;code&gt;panic!&lt;/code&gt;, an irrecoverable error during compilation time should invoke &lt;code&gt;compile_error!&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Replacing the &lt;code&gt;todo!&lt;/code&gt; with code that adds a &lt;code&gt;compiler_error!&lt;/code&gt; invocation to the token stream is a good start: no wall of text and a helpful message. Still, the error only tells us something went wrong during the macro derivation.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;error: invalid repr
  --&amp;gt; lib.rs:28:17
   |
28 | #[derive(Debug, Shareable)]
   |                 ^^^^^^^^^
   |
   = note: this error originates in the derive macro `Shareable` (in Nightly builds, run with -Z macro-backtrace for more info)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;a href="https://docs.rs/syn/latest/syn/" rel="noopener noreferrer"&gt;&lt;code&gt;syn&lt;/code&gt; crate&lt;/a&gt; gives us a better way to represent errors using &lt;code&gt;syn::Error&lt;/code&gt;: a type that can hold a &lt;code&gt;Span&lt;/code&gt; along with a message. For our purposes, a &lt;code&gt;Span&lt;/code&gt; is something that holds a start and an end index into a string, identifying a range of characters contained in the source code. For example, on the following snippet the &lt;code&gt;Span&lt;/code&gt; could be used to determine where the markers (&lt;code&gt;^&lt;/code&gt;) should start and where they should end.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;28 | #[derive(Debug, Shareable)]
   |                 ^^^^^^^^^
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When parsing a literal into a repr, we have access to the span of that literal, and that allows us to create an error that along with the message also stores where in the source code our typo happened. Here is that snippet again, now much closer to the &lt;a href="https://github.com/voxelstack/atlas/blob/f6b16ab1b759cb7a2a74154198ba12089cfbbb7d/src/atlas/comms_derive/src/attrs.rs#L128-L133" rel="noopener noreferrer"&gt;actual implementation&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;match&lt;/span&gt; &lt;span class="n"&gt;lit&lt;/span&gt;&lt;span class="nf"&gt;.to_string&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.as_ref&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;raw&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Repr&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Raw&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;serde&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Repr&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Serde&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;shareable&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Ok&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Repr&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Shareable&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
     &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;Err&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;syn&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="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;lit&lt;/span&gt;&lt;span class="nf"&gt;.span&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="s"&gt;"invalid repr, expected literal: &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;raw&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;, &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;serde&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;, or &lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;shareable&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, the typo will cause a compiler error specifying what went wrong, which token that caused it, and how to fix it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;error: invalid repr, expected literal: "raw", "serde", or "shareable"
  --&amp;gt; src\lib.rs:24:24
   |
24 |     #[shareable(repr = "rraw", transfer)]
   |                        ^^^^^^

error: could not compile `example` (lib) due to previous error
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Handling macro derivation errors with &lt;code&gt;Result&lt;/code&gt;
&lt;/h5&gt;

&lt;p&gt;To render a &lt;code&gt;syn::Error&lt;/code&gt; into a token stream that invokes &lt;code&gt;compiler_error!&lt;/code&gt; we use &lt;code&gt;syn::Error::into_compile_error&lt;/code&gt;. &lt;a href="https://github.com/dtolnay/syn/blob/b387b13f48e76468a3fc58e55559d2f41fa28a71/src/error.rs#L275-L322" rel="noopener noreferrer"&gt;Here is how that happens&lt;/a&gt;, note how it gets the &lt;code&gt;start&lt;/code&gt; and &lt;code&gt;end&lt;/code&gt; from the &lt;code&gt;Span&lt;/code&gt; and uses those when constructing the token stream.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Little fun fact: &lt;code&gt;syn&lt;/code&gt; uses &lt;code&gt;Delimiter::Brace&lt;/code&gt; when constructing the group, so this generates &lt;code&gt;::core::compile_error!{$message}&lt;/code&gt;. Delimiters don't matter for &lt;code&gt;macro_rules!&lt;/code&gt; macros: for example, &lt;code&gt;vec!(1, 2, 3)&lt;/code&gt;, &lt;code&gt;vec! [1, 2, 3]&lt;/code&gt;, and &lt;code&gt;vec!{1, 2, 3}&lt;/code&gt; are all valid.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;You can delegate your macro derivation to a function that returns a &lt;code&gt;Result&lt;/code&gt;, and then consume it with &lt;code&gt;unwrap_or_else&lt;/code&gt; to render any &lt;code&gt;syn::Error&lt;/code&gt; that might happen during derivation. Here's a copy-pastable snippet:&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;#[proc_macro_derive(Shareable,&lt;/span&gt; &lt;span class="nd"&gt;attributes(shareable))]&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;derive_shareable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;proc_macro&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TokenStream&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;proc_macro&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TokenStream&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;ast&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nd"&gt;parse_macro_input!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;DeriveInput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expand_derive_shareable&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;ast&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="nf"&gt;.unwrap_or_else&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;syn&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;into_compile_error&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="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;expand_derive_shareable&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ast&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="nn"&gt;syn&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;DeriveInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nn"&gt;syn&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nb"&gt;Result&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nn"&gt;proc_macro2&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;TokenStream&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;//--snip--&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Notice the &lt;code&gt;proc_macro2::TokenStream&lt;/code&gt; as opposed to &lt;code&gt;proc_macro::TokenStream&lt;/code&gt;. Types from &lt;code&gt;proc_macro&lt;/code&gt; can only exist inside procedural macros, which is why &lt;code&gt;syn&lt;/code&gt; uses &lt;code&gt;proc_macro2&lt;/code&gt; instead. Keep an eye out for this if you ever come across errors saying you "can't use &lt;code&gt;TokenStream&lt;/code&gt; and must use &lt;code&gt;TokenStream&lt;/code&gt; instead".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Making requests
&lt;/h3&gt;

&lt;p&gt;With Comlink, it's possible to call a function from the webworker and await the return on the main thread. Wrapping message passing instead of implementing an RPC server was a choice, still we can use what we've developed so far to await responses.&lt;/p&gt;

&lt;p&gt;Let's define what we want to achieve: the client should have an async function capable of taking a &lt;code&gt;Shareable&lt;/code&gt; message, sending it to the server, awaiting a &lt;code&gt;Shareable&lt;/code&gt; response, and returning it.&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;// types.rs&lt;/span&gt;
&lt;span class="nd"&gt;#[derive(Debug,&lt;/span&gt; &lt;span class="nd"&gt;Shareable)]&lt;/span&gt;
&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;ClientRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Ping&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;Shareable)]&lt;/span&gt;
&lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="n"&gt;ServerResponse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Pong&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// client.rs&lt;/span&gt;
&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ClientRequest&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;ServerResponse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nd"&gt;todo!&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;ClientRequest&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Ping&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nd"&gt;trace!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"client-&amp;gt;server: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nd"&gt;trace!&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"client&amp;lt;-server: {:?}"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&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;How should we implement &lt;code&gt;request&lt;/code&gt;? Before sending a message, a listener must be ready to receive a response and to set that up we can call &lt;code&gt;add_listener&lt;/code&gt; on the sender port. Upon receiving the response it's easy to get back the &lt;code&gt;ServerResponse&lt;/code&gt; using &lt;code&gt;try_into&lt;/code&gt; (whose implementation we didn't have to write because &lt;code&gt;ServerResponse&lt;/code&gt; is &lt;code&gt;Shareable&lt;/code&gt;). With a listener set up, the client then sends a message to the server, waits for a response, and returns it (without forgetting to clear the listener).&lt;/p&gt;

&lt;p&gt;Before looking at the Rust code it's useful to think about how the implementation would look like using pseudo-code and promises. Using JavaScript to write that pseudo-code, it looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clientMessage&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="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;_reject&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;onResponse&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;once&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;clientMessage&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;blockquote&gt;
&lt;p&gt;Small tip: when prototyping, it's a good idea to be aware of optional parameters being ignored. The Rust compiler has that covered, but with JavaScript the &lt;code&gt;no-unused-vars&lt;/code&gt; lint rule makes that difficult, even if you are willing to do it manually. For example, &lt;code&gt;new Promise(resolve, _reject) ...&lt;/code&gt; causes a lint error if &lt;code&gt;_reject&lt;/code&gt; is not used. However, &lt;a href="https://github.com/voxelstack/atlas/blob/f6b16ab1b759cb7a2a74154198ba12089cfbbb7d/.eslintrc.cjs#L31-L39" rel="noopener noreferrer"&gt;the lint rule accepts an ignore pattern&lt;/a&gt;, and can be made to accept this as long as the unused variable name matches that pattern (&lt;code&gt;^_&lt;/code&gt;).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;a href="https://docs.rs/js-sys/0.3.64/js_sys/struct.Promise.html" rel="noopener noreferrer"&gt;&lt;code&gt;js_sys&lt;/code&gt; has bindings for &lt;code&gt;Promise&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://docs.rs/wasm-bindgen-futures/latest/wasm_bindgen_futures/" rel="noopener noreferrer"&gt;wasm-bindgen-futures&lt;/a&gt; provides a bridge between JavaScript promises and Rust futures, meaning this can be translated to Rust. Dealing with the reject and resolve closures would be extra mental overhead, and I had already done some tests that involved installing &lt;a href="https://tokio.rs/" rel="noopener noreferrer"&gt;tokio&lt;/a&gt;, so for the sake of this proof of concept using channels was faster.&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;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ClientMessage&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;ServerResponse&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tx&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;rx&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nn"&gt;channel&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;ServerResponse&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;listener&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;self&lt;/span&gt;
        &lt;span class="py"&gt;.port&lt;/span&gt;
        &lt;span class="nf"&gt;.add_listener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;Closure&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;move&lt;/span&gt; &lt;span class="p"&gt;|&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;MessageEvent&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;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ServerResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="nf"&gt;.data&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="nf"&gt;.try_into&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="n"&gt;tx&lt;/span&gt;&lt;span class="nf"&gt;.try_send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;payload&lt;/span&gt;&lt;span class="py"&gt;.message&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
                &lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Request channel should not be closed."&lt;/span&gt;&lt;span class="p"&gt;);&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;.pipe&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;message&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;rx&lt;/span&gt;&lt;span class="nf"&gt;.recv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;&lt;span class="nf"&gt;.expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Should post a valid response."&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;listener&lt;/span&gt;&lt;span class="nf"&gt;.clear&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

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

&lt;/div&gt;



&lt;p&gt;It almost works, but if the client makes multiple concurrent requests it will have no way of associating them with one of the incoming responses. That issue can be solved by &lt;a href="https://github.com/voxelstack/atlas/blob/f6b16ab1b759cb7a2a74154198ba12089cfbbb7d/src/atlas/client/src/lib.rs#L99-L122" rel="noopener noreferrer"&gt;including a random request id&lt;/a&gt; that &lt;a href="https://github.com/voxelstack/atlas/blob/f6b16ab1b759cb7a2a74154198ba12089cfbbb7d/src/atlas/server/src/lib.rs#L35-L81" rel="noopener noreferrer"&gt;the server copies over to the response&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  The tally counter
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://github.com/GoogleChromeLabs/comlink#running-a-simple-function" rel="noopener noreferrer"&gt;Comlink uses a counter as an example&lt;/a&gt;, and we are now ready to replicate that. The client should be able to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;query the current value;&lt;/li&gt;
&lt;li&gt;increment or decrement the value;&lt;/li&gt;
&lt;li&gt;set the value.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;An enum can represent those operations:&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;// types.rs&lt;/span&gt;
&lt;span class="nd"&gt;#[derive(Debug,&lt;/span&gt; &lt;span class="nd"&gt;Shareable)]&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;ClientMessage&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Inc&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;Dec&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="nd"&gt;#[shareable(repr&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"serde"&lt;/span&gt;&lt;span class="nd"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The client still needs some glue code that has to be written manually (this impl block could also be generated automatically using derive macros).&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;// client.rs&lt;/span&gt;
&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Client&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ClientMessage&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;inc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ClientMessage&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Inc&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;dec&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ClientMessage&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Dec&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;pub&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;value&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="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;u8&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="nf"&gt;.request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nn"&gt;ClientMessage&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="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;&lt;span class="k"&gt;.await&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The server then must store a value and either return or mutate it depending on the request. I am leaving out the details on &lt;a href="https://github.com/voxelstack/atlas/blob/f6b16ab1b759cb7a2a74154198ba12089cfbbb7d/src/atlas/server/src/lib.rs#L35-L81" rel="noopener noreferrer"&gt;how the server receives, processes, and responds to messages&lt;/a&gt; (where I also used tokio channels) so we can focus on responding to a request:&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;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;match&lt;/span&gt; &lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nn"&gt;ClientMessage&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nn"&gt;ServerMessage&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Query&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;.counter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nn"&gt;ClientMessage&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Inc&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.counter&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="nn"&gt;ServerMessage&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Query&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;.counter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nn"&gt;ClientMessage&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Dec&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.counter&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="nn"&gt;ServerMessage&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;Query&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;.counter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="nn"&gt;ClientMessage&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="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;self&lt;/span&gt;&lt;span class="py"&gt;.counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;ServerMessage&lt;/span&gt;&lt;span class="nf"&gt;.Query&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;.counter&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;After all this work, we can finally write some Svelte and create a working demo. After initializing wasm-bindgen on the main thread, instantiate the web worker and give its handle to the client, which allows us to perform requests. The worker needs to initialize wasm-bindgen and the rayon thread pool. For that reason, creating the worker is done through a &lt;a href="https://github.com/voxelstack/atlas/blob/f6b16ab1b759cb7a2a74154198ba12089cfbbb7d/src/lib/spawner.ts#L1-L14" rel="noopener noreferrer"&gt;utility function&lt;/a&gt; returning a promise that only resolves after the worker initialization finishes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    //--snip--
    let client: Client;

    onMount(async () =&amp;gt; &lt;span class="si"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;initWasm&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// spawn returns a promise that resolves after the worker&lt;/span&gt;
        &lt;span class="c1"&gt;// has finished initializing wasm-bindgen and rayon.&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="si"&gt;}&lt;/span&gt;);
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dec&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;-&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;query&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;GET&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inc&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;+&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Addition and subtraction without blocking the main thread! More importantly, no need to think about Rust when writing JavaScript, and no need to think about JavaScript when writing Rust.&lt;/p&gt;

&lt;h3&gt;
  
  
  Finishing touches
&lt;/h3&gt;

&lt;p&gt;It would be nice to show the value on the DOM instead of the console, and we could already do that by manually updating a DOM node whenever &lt;code&gt;dec&lt;/code&gt;, &lt;code&gt;inc&lt;/code&gt;, or &lt;code&gt;set&lt;/code&gt; are called. Using any modern web framework in the past couple of years will tell you that there is a better way of doing that. Svelte &lt;a href="https://learn.svelte.dev/tutorial/reactive-declarations" rel="noopener noreferrer"&gt;automatically updates the DOM when a component's state changes&lt;/a&gt;, and ideally we want to be able to write &lt;code&gt;&amp;lt;span&amp;gt;{counter}&amp;lt;/span&amp;gt;&lt;/code&gt; and call it a day.&lt;/p&gt;

&lt;p&gt;Before this experiment, I did some tests with wasm-bindgen running on the main thread of a React application. The same issue showed itself, and the solution wasn't trivial: React can't see state changes inside a wasm-bindgen object because the reference to that object stays the same. The first solution that came to mind was to give Rust a function that could run whenever a state change happened and update React state from that function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Counter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;wasmState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setWasmState&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;counter&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="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;wasmStateDiff&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setWasmState&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;previousState&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="nx"&gt;previousState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;wasmStateDiff&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;_wasmCounter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useRef&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;WasmCounter&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="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;wasmState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;You can imagine WasmCounter calling &lt;code&gt;onChange({ counter: newValue })&lt;/code&gt; whenever it changes the counter value.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I didn't put a lot of thought or research time into this at the time, but it worked so now with Svelte I started by considering a similar approach. Things looked much worse though: now the data lives on a web worker and is always at least one &lt;code&gt;postMessage&lt;/code&gt; away.&lt;/p&gt;

&lt;p&gt;That was when I came across a seemingly unrelated &lt;a href="https://timdeschryver.dev/blog/unlocking-reactivity-with-svelte-and-rxjs" rel="noopener noreferrer"&gt;article about using RxJs with Svelte&lt;/a&gt;. It is a good look into how Svelte reactivity works and very much worth a read, but here is the main takeaway: an RxJs Observable serves as a Svelte store (and by extension, so does any observable).&lt;/p&gt;

&lt;h4&gt;
  
  
  A way to describe observables
&lt;/h4&gt;

&lt;blockquote&gt;
&lt;p&gt;For completeness, I want to briefly explain how observables are used here. Check &lt;a href="https://rxjs.dev/guide/observable" rel="noopener noreferrer"&gt;the RxJs docs on observables&lt;/a&gt; for a proper description.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;For our purposes, an observable is something that can notify you about changes in a value. To tell the observable you want to be notified you subscribe to it, and to subscribe you provide a callback that will receive those notifications. Subscribing also gives you an unsubscribe function for when you want to stop observing the value. Some code:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Observable&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="nf"&gt;setInterval&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;counter&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="nx"&gt;counter&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="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="mi"&gt;1000&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;unsubscribe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;counter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`counter changed to: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//--snip--&lt;/span&gt;

&lt;span class="c1"&gt;// Stop observing the value.&lt;/span&gt;
&lt;span class="nf"&gt;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Server events
&lt;/h4&gt;

&lt;p&gt;We still cannot use this knowledge unless the server has a way to notify the client of value changes. In practice, all we need is for the server to post a message with some sort of event descriptor (WebSockets are a good analogy for 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;Shareable)]&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;ServerEvent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nd"&gt;#[shareable(repr&lt;/span&gt; &lt;span class="nd"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"serde"&lt;/span&gt;&lt;span class="nd"&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, there should be a way to subscribe to these events and since there might be many types of events, subscribing needs an ID specifying which event we are subscribing to. I used the enum name and variant name separated by &lt;code&gt;::&lt;/code&gt;, since that matches how you would describe the Rust type. For example, to subscribe to the counter change events, you would use &lt;code&gt;ServerEvent :: Count&lt;/code&gt; as the id.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;The spaces before and after &lt;code&gt;::&lt;/code&gt; are to match Rust's &lt;a href="https://doc.rust-lang.org/std/macro.stringify.html" rel="noopener noreferrer"&gt;&lt;code&gt;stringify!&lt;/code&gt;&lt;/a&gt;: a macro that turns a list of tokens into a &lt;code&gt;&amp;amp; 'static str&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="c1"&gt;// client.rs&lt;/span&gt;
    &lt;span class="c1"&gt;//--snip--&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;observe&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="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;String&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;Observable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nd"&gt;todo!&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;Like we did with the requests, before talking about &lt;a href="https://github.com/voxelstack/atlas/blob/f6b16ab1b759cb7a2a74154198ba12089cfbbb7d/src/atlas/client/src/lib.rs#L142-L169" rel="noopener noreferrer"&gt;the Rust implementation&lt;/a&gt; let's see how we'd implement an observable for these server events using pseudo code written in JavaScript.&lt;/p&gt;

&lt;p&gt;The observable needs to store the ID of the events it cares about and a port that receives events. When subscribe is called, it gets a function that will be used to notify the subscriber of changes. That function has to be stored and called whenever the value changes. Finally, it needs to return an unsubscribe function. It looks like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Observable&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listeners&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;

        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;port&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&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;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="nx"&gt;listeners&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;forEach&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onChange&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;newValue&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="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listeners&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listeners&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;listeners&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;l&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;l&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;onChange&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="nx"&gt;unsubscribe&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;blockquote&gt;
&lt;p&gt;Notice how &lt;code&gt;onChange&lt;/code&gt; will not be called when we first subscribe, meaning the subscriber won't know the current value unless it changes. That can be fixed by either querying the current value whenever a new subscriber is added or keeping a local copy of the value on the observable. I went with the first solution, which is the simplest.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Here is the Rust API:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight rust"&gt;&lt;code&gt;&lt;span class="k"&gt;impl&lt;/span&gt; &lt;span class="n"&gt;Observable&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;subscribe&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="k"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;on_change&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nn"&gt;js_sys&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Function&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;JsValue&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;//--snip--&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Helpful tip: to return a wasm-bindgen closure to JavaScript and transfer the memory management responsibility from Rust to the JavaScript garbage collector, call &lt;a href="https://docs.rs/wasm-bindgen/latest/wasm_bindgen/closure/struct.Closure.html#method.into_js_value" rel="noopener noreferrer"&gt;&lt;code&gt;into_js_value&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h5&gt;
  
  
  The hacky part
&lt;/h5&gt;

&lt;p&gt;I didn't talk about the Rust implementation of an observable, and the reason for that is simple: it's hacky.&lt;/p&gt;

&lt;p&gt;By this point, I had already achieved my initial goal but only had a boring console demo to show for it. Observables are something I added later to put together a more interesting demo. That was when I came across one of the most practical ideas of this entire project (reactivity with Svelte + WebAssembly). So instead of skipping it I will quickly go over why this part of the implementation is hacky.&lt;/p&gt;

&lt;p&gt;An observable should be able to hold a reference to a port and receive events through there. That would require the observable to have a lifetime parameter, but structs shared with JavaScript cannot have lifetime or type parameters (a wasm-bindgen limitation). Cloning the port would solve this, but the port holds a &lt;code&gt;Box&amp;lt;dyn RawPort&amp;gt;&lt;/code&gt; which is not &lt;code&gt;Clone&lt;/code&gt;. Another solution is to transmute away the lifetime, and I wanted to avoid doing that.&lt;/p&gt;

&lt;p&gt;To get around this, I decided to create a &lt;code&gt;MessageChannel&lt;/code&gt; that receives the server events and then forwards then to a &lt;code&gt;BroadcastChannel&lt;/code&gt;, which can easily be accessed from anywhere using just a string ID. This was also an opportunity to &lt;a href="https://github.com/voxelstack/atlas/blob/f6b16ab1b759cb7a2a74154198ba12089cfbbb7d/src/atlas/comms/src/port.rs#L58-L82" rel="noopener noreferrer"&gt;implement &lt;code&gt;RawPort&lt;/code&gt; for a new type&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;In the end, it was unavoidable to &lt;a href="https://github.com/voxelstack/atlas/blob/f6b16ab1b759cb7a2a74154198ba12089cfbbb7d/src/atlas/client/src/lib.rs#L65-L73" rel="noopener noreferrer"&gt;transmute away a lifetime&lt;/a&gt; so I could keep a handle to this forwarding listener. By "transmuting away a lifetime" I mean changing the lifetime to &lt;code&gt;'static&lt;/code&gt;. Very rarely will that be the correct thing to do, and if done incorrectly it will leak memory. So don't do it (unless you're writing a demo).&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Putting it all together
&lt;/h3&gt;

&lt;p&gt;That was quite the journey for a tally counter, but all the pieces are in place now.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;There is &lt;a href="https://github.com/vitejs/vite/issues/13367" rel="noopener noreferrer"&gt;an issue with Vite that prevents me from building this&lt;/a&gt;, so sadly I cannot give you a link with the working counter. I encourage you to clone the repo and play around with it, &lt;a href="https://github.com/voxelstack/atlas#running" rel="noopener noreferrer"&gt;building and running this is surprisingly easy&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let's start by describing what we want to achieve:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the current value of the counter should be shown on the DOM;&lt;/li&gt;
&lt;li&gt;there should be buttons to increment and decrement the counter;&lt;/li&gt;
&lt;li&gt;you should be able to subscribe and unsubscribe from a logger, which prints every counter change to the console.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;And then we can put it all together: the +/- buttons just need to call the corresponding client functions. To show the value on the DOM, we create an observable from the &lt;code&gt;ServerEvent::Count&lt;/code&gt; events. And finally, we subscribe to that observable with a function that logs changes to the console, storing the unsubscribe function for later.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt; &lt;span class="na"&gt;lang&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"ts"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    //--snip--    
    let client: Client;
    let count: Observable;
    let unsubscribe: Function | undefined;

    const logger = (value: number) =&amp;gt; console.log(`observed: $&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;`);

    onMount(async () =&amp;gt; &lt;span class="si"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;initWasm&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

        &lt;span class="c1"&gt;// spawn returns a promise that resolves after the worker&lt;/span&gt;
        &lt;span class="c1"&gt;// has finished initializing wasm-bindgen and rayon.&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;server&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;spawn&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;server&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;observe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;ServerEvent :: Count&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;unsubscribe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="si"&gt;}&lt;/span&gt;);
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;script&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dec&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;-&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;$count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;inc&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;+&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;

&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
    &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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="nx"&gt;unsubscribe&lt;/span&gt;&lt;span class="p"&gt;?.();&lt;/span&gt;
        &lt;span class="nx"&gt;unsubscribe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;detach logger&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;
    &lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="na"&gt;click&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&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="nx"&gt;unsubscribe&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;logger&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;attach logger&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fljnkg2cyh13w4kn1zby7.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fljnkg2cyh13w4kn1zby7.gif" alt="A recording of a website with the finished tally counter. It shows the current value, a button with a minus sign to the left, and a button with a plus sign to the right. Under the  value are buttons called "&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's take a moment to appreciate this line: &lt;code&gt;&amp;lt;span&amp;gt;{$count}&amp;lt;/span&amp;gt;&lt;/code&gt;. &lt;code&gt;count&lt;/code&gt; lives on our server: Rust code running on a web worker. And yet whenever it changes, this span updates. Clicking a button will use a Rust client running on the main thread to post a message, which tells a Rust server running on a web worker to mutate some state. After the mutation, the server will generate a change event, post a message to the client, and update the DOM. I hope you find that as amazing (and useful) as I do.&lt;/p&gt;

</description>
      <category>rust</category>
      <category>webassembly</category>
    </item>
  </channel>
</rss>
