<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Stories by Paulo Cuchi on Medium]]></title>
        <description><![CDATA[Stories by Paulo Cuchi on Medium]]></description>
        <link>https://medium.com/@paulo.cuchi?source=rss-aef99a3c82ca------2</link>
        <image>
            <url>https://cdn-images-1.medium.com/fit/c/150/150/0*9CYLkWCgFcRQuaU2.jpg</url>
            <title>Stories by Paulo Cuchi on Medium</title>
            <link>https://medium.com/@paulo.cuchi?source=rss-aef99a3c82ca------2</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sun, 15 Nov 2026 01:27:06 GMT</lastBuildDate>
        <atom:link href="https://medium.com/@paulo.cuchi/feed" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com/" rel="hub"/>
        <item>
            <title><![CDATA[Go vs Rust: Writing a CLI tool]]></title>
            <link>https://medium.com/magrathealabs/go-vs-rust-writing-a-cli-tool-231accadcc88?source=rss-aef99a3c82ca------2</link>
            <guid isPermaLink="false">https://medium.com/p/231accadcc88</guid>
            <category><![CDATA[benchmark]]></category>
            <category><![CDATA[go]]></category>
            <category><![CDATA[rust]]></category>
            <category><![CDATA[programming]]></category>
            <dc:creator><![CDATA[Paulo Cuchi]]></dc:creator>
            <pubDate>Wed, 05 Aug 2020 13:43:24 GMT</pubDate>
            <atom:updated>2020-08-05T13:43:24.319Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/640/0*AH6fP575WmhnHQ2l.png" /></figure><p>This text is about my adventure writing a small CLI application (twice) using two languages I had little experience with.</p><p>If you are eager to jump right into the code and compare it yourself, check it out the <a href="https://github.com/cuchi/hashtrack/tree/master/cli-go">Go source</a> and the <a href="https://github.com/cuchi/hashtrack/tree/master/cli-rust">Rust source</a>.</p><h3>About the Project</h3><p>I have a <em>pet project</em> called Hashtrack, which is a full-stack web application I wrote for a technical interview. This project is rather small and it is simple to use:</p><ol><li>You authenticate — considering you already created your account</li><li>You input hashtags you want to track</li><li>You wait for the <em>captured</em> tweets to show on your screen</li></ol><p><a href="https://hashtrack.herokuapp.com/">Check it out here.</a></p><p>After my interview, I kept improving this project just for fun, and I noticed that it could be a perfect place to test my skills by implementing a CLI tool. I already had the server, so I just needed to pick a language to implement a small set of features under my project’s API.</p><h3>Features</h3><ul><li>hashtrack login - Creates a session token and store it in the local filesystem in a config file.</li><li>hashtrack logout - Remove the locally stored session token.</li><li>hashtrack track &lt;hashtag&gt; [...] - Tracks one or more hashtags.</li><li>hashtrack untrack &lt;hashtag&gt; [...] - Untracks one or more previously tracked hashtags.</li><li>hashtrack tracks - Displays the hashtags you are tracking.</li><li>hashtrack list - Displays the latest 50 captured tweets.</li><li>hashtrack watch - Stream and display the captured tweets in real-time.</li><li>hashtrack status - Displays who you are, if logged in.</li><li>Should have an --endpoint option to point the CLI to another server.</li><li>Should have a --config option to load a custom config file.</li><li>This config file could also share the endpoint property.</li></ul><p>What we have to know beforehand:</p><ul><li>The CLI should use the project’s API, which is GraphQL under HTTP + WebSockets.</li><li>The CLI should use the filesystem to store a config file.</li><li>The CLI should parse positional arguments and flags.</li></ul><h3>How did I end up using Go and Rust?</h3><p>There is a large set of languages you can use to write CLI tools.</p><p>In this case, I wanted a language I had little or no prior experience with, I also wanted one that could easily compile to a native executable, which is a nice perk to have on a CLI tool.</p><p>My first obvious choice was Go, for some reason. But I also had little experience with Rust, and I saw it could also be a good fit for this project. So… why not both? Since my main objective here is to learn, could be a great opportunity to implement this project twice and find what are the <em>pros and cons</em> of each one from my point of view.</p><blockquote><em>Honorable mentions to </em><a href="https://crystal-lang.org/"><em>Crystal</em></a><em> and </em><a href="https://nim-lang.org/"><em>Nim</em></a><em>, those were very promising options too. I’m looking forward to learn about them in another pet project.</em></blockquote><h3>Local environment</h3><p>The first thing I look when using a new toolset is whether it has an easy way to make it available for my user, without using the distribution package manager to install it system-wide. We are talking about version managers, they make our life easier by installing the tools in a user-wide manner instead of system-wide. <a href="https://github.com/nvm-sh/nvm">NVM</a> for Node.js does it very well.</p><p>When using Go, there is the <a href="https://github.com/moovweb/gvm">GVM</a> project which handles the local install &amp; version management, and it is easy to setup:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/5ed2d15c017e9e5cd55bba2e37bc78c5/href">https://medium.com/media/5ed2d15c017e9e5cd55bba2e37bc78c5/href</a></iframe><p>There is also two environment variables we need to know, they are GOROOT and GOPATH -- You can read more about them <a href="https://www.jetbrains.com/help/go/configuring-goroot-and-gopath.html">here</a>.</p><p>The first <em>problem</em> I found using Go, was when I was figuring out how the module resolution worked along with the GOPATH, it became quite frustrating to setup a project structure with a functional local development environment.</p><p>In the end, I just used GOPATH=$(pwd) in my project&#39;s directory, the main perk was to have a per-project dependency setup, like a node_modules. It worked well.</p><blockquote><em>After finishing my project, I found out that </em><a href="https://github.com/GetStream/vg"><em>virtualgo</em></a><em> existed and would solve my problems with </em><em>GOPATH.</em></blockquote><p>Rust has an official project called <a href="https://rustup.rs/">rustup</a>, which manages the Rust installation, also known as <em>toolchain</em>. It can be easily set up with a one-liner. Also, there is a set of optional components using rustup, such as the <a href="https://github.com/rust-lang/rls">rls</a> and <a href="https://github.com/rust-lang/rustfmt">rustfmt</a>. Many projects requires a <em>nightly</em> version of the Rust toolchain, with rustup there was no problem switching between the versions.</p><h4>Editor Support</h4><p>For both of the languages, editor tooling was flawless, as a VSCode user, I can find extensions for both Go and Rust in the marketplace.</p><p>When debugging with Rust, I had to install the <a href="https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb">CodeLLDB</a> extension after following <a href="https://www.forrestthewoods.com/blog/how-to-debug-rust-with-visual-studio-code/">this tutorial</a>.</p><h3>Package management</h3><p>Go doesn’t have a package manager or even an official registry. Instead, its module resolution works in a way you can import them from external URLs.</p><p>For dependency management, Rust uses the Cargo, which downloads and compiles dependencies from <a href="https://crates.io/">crates.io</a>, which is the official registry for Rust packages. Packages inside the Crates ecosystem can also have their documentation available in <a href="https://docs.rs/">docs.rs</a></p><h3>Libraries</h3><p>My first objective was to see how easy could be to implement a simple GraphQL query/mutation over HTTP.</p><p>For the Go language, I found some libraries, like <a href="https://github.com/machinebox/graphql">machinebox/graphql</a> and <a href="https://github.com/shurcooL/graphql">shurcooL/graphql</a>, the second one uses structs for (un) marshaling the data, that is what made me stick to it.</p><blockquote><em>I actually used a fork of shurcooL/graphql, because I needed to set the </em><em>Authorization header in the client, the changes are in </em><a href="https://github.com/shurcooL/graphql/pull/48"><em>this pull request</em></a><em>.</em></blockquote><p>This is the Go example of an GraphQL mutation call:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/3fb20c07666c0d0d618e0496f566800b/href">https://medium.com/media/3fb20c07666c0d0d618e0496f566800b/href</a></iframe><p>In Rust, I had to use two libraries to make GraphQL calls. That is because graphql_client is protocol-agnostic, it only focus on code generation for serializing and deserializing data. So I needed a second library ( reqwest) to take care of the HTTP requests.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/398daf84a0d41e015eea5dca73c9724b/href">https://medium.com/media/398daf84a0d41e015eea5dca73c9724b/href</a></iframe><p>Neither of the libraries for Go and Rust had any implementation for GraphQL via websocket protocol.</p><p>In fact, graphql_client for Rust supports <em>Subscriptions</em>, but since it is protocol-agnostic, I had to implement the whole GraphQL WebSocket communication on my own, <a href="https://github.com/cuchi/hashtrack/blob/b5a75f4368837cd51c621b6560a03e1835ec4e5b/cli-rust/src/tweet.rs#L90">check it out</a>.</p><p>In order to use WebSockets in the Go version, the library should be modified to support the protocol. Since I was already using a fork of the library, I didn’t feel like doing it. Instead, I used a <em>poor man’s</em> way of “watching” the new tweets, which was to request the API every 5 seconds to retrieve them, <a href="https://github.com/cuchi/hashtrack/blob/b5a75f4368837cd51c621b6560a03e1835ec4e5b/cli-go/src/hashtrack/tweets/tweets.go#L65">I’m not proud of it</a>.</p><p>Using Go, there is the go keyword to spawn a lightweight thread, also called <em>goroutine</em>. In contrast, Rust uses operating system threads by calling a Thread::spawn. Besides that, both implementations uses channels to transfer objects between their threads.</p><h3>Error handling</h3><p>In Go, errors are treated just like any other value. The common way to handle errors in Go, is just to check if they are present.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/c7d011ef55a2a37dfc2ae64ffead0a50/href">https://medium.com/media/c7d011ef55a2a37dfc2ae64ffead0a50/href</a></iframe><p>Rust has the Result&lt;T, E&gt; enum, which can encapsulate an Ok(T) for success, or an Err(E) for errors. It also has the Option&lt;T&gt; enum, with Some(T) or None. If you are familiar with Haskell, you may recognize those as the Either and the Maybe monads.</p><p>There is also a syntatic sugar for error propagation (the ? operator) that resolves the value from the Result or Option structure, automatically returning Err(...) or None when something goes bad.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/90828390c042b19db0c24fefebef8ff2/href">https://medium.com/media/90828390c042b19db0c24fefebef8ff2/href</a></iframe><p>The code above is the equivalent of</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/7ae6a9d665c65d53dd2185d8ab674d10/href">https://medium.com/media/7ae6a9d665c65d53dd2185d8ab674d10/href</a></iframe><p>Rust has:</p><ul><li>monadic constructs (Option &amp; Result)</li><li>the error propagation operator</li><li>the From trait, to automatically convert errors on propagation</li></ul><p>The combination of the three features above makes up the best error handling solution I saw in a language, being simple, sound and maintainable</p><p>at the same time.</p><h3>Compilation time</h3><p>Go is build with fast compilation time as a critical requirement, let’s see:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8011e1b0fdfca791a8a74e0f7a534d6e/href">https://medium.com/media/8011e1b0fdfca791a8a74e0f7a534d6e/href</a></iframe><p>That’s impressive, let’s see how Rust does this:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/e4ba12fad4bd2e16fc00c7b29b18ea10/href">https://medium.com/media/e4ba12fad4bd2e16fc00c7b29b18ea10/href</a></iframe><p>It compiled all the dependencies, which are 214 modules in total. When we run it again, everything is already compiled, so it runs instantly:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/2405dec087ee65247a73c7fa7cd77366/href">https://medium.com/media/2405dec087ee65247a73c7fa7cd77366/href</a></iframe><p>As you can see, Rust uses an incremental compilation model, which partially recompiles the module dependency tree, starting from changed modules until it propagates into its dependents.</p><p>If you are doing a release build, it takes longer, which is expected because of the optimization tasks the compiler do internally:</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/409828737dc988f84682d13fd95ffb39/href">https://medium.com/media/409828737dc988f84682d13fd95ffb39/href</a></iframe><h4>Continuous Integration</h4><p>As you would expect, the time differences show up on the CI workflow:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/687/0*0Sqk61_poiUxyxVA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/682/0*9sT0HG7DXbH0yFtM.png" /></figure><h3>Memory usage</h3><p>To measure memory usage, I used /usr/bin/time -v ./hashtrack list for each of the versions. time -v displays a lot of interesting info, but here we are looking for the <strong>Maximum resident set size</strong> of the process, which is the peak amount of allocated physical memory during the execution.</p><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/0ae2c5fd9ab5380e36c5d5c7c0c0e6eb/href">https://medium.com/media/0ae2c5fd9ab5380e36c5d5c7c0c0e6eb/href</a></iframe><h4>Go</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/8232c7b761bf52018318c1140d4d338c/href">https://medium.com/media/8232c7b761bf52018318c1140d4d338c/href</a></iframe><h4>Rust</h4><iframe src="" width="0" height="0" frameborder="0" scrolling="no"><a href="https://medium.com/media/ce61b9eb5f7faf833cf85aa4fd8abdc8/href">https://medium.com/media/ce61b9eb5f7faf833cf85aa4fd8abdc8/href</a></iframe><p>This memory usage accounts for the task of:</p><ul><li>interpreting system arguments</li><li>loading &amp; parsing a configuration file from the filesystem</li><li>calling GraphQL over HTTP over TLS</li><li>parsing a JSON response</li><li>writing the formatted data to stdout</li></ul><p>Both languages have different ways to manage memory and allocations.</p><p>Go has a garbage collector, which is a common way to track down unused heap memory and reclaim it instead of doing this manually. Since garbage collectors are a composition of heuristics, there are always tradeoffs, generally between performance and memory usage.</p><p>Rust memory model has concepts like <em>ownership</em>, <em>borrowing</em> and <em>lifetimes</em>, which not only helps with memory safety, but also guarantee total control of the program heap memory without manual management or a garbage collector.</p><p>For comparison, let’s take some other executables which do a rather <em>similar</em> task:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/663/1*CnwpqdN6vIybDzJ7_vL2Ww.png" /></figure><h3>Conclusion</h3><p>They were both very great tools for the job. But of course, they have different priorities. On one side, we have an option which tries to keep software development simple, maintainable and accessible. On the other hand, we have a language focused on soundness, safety, and performance.</p><h4>Reasons I would use Go</h4><ul><li>I want a very simple language for my teammates to learn</li><li>I want little flexibility, to write plain and simple code</li><li>If I build exceptionally/mostly for Linux</li><li>If compilation time is an issue</li><li>I want mature asynchronous semantics</li></ul><h4>Reasons I would use Rust</h4><ul><li>I want state-of-the-art error handling for my code</li><li>I want a multi-paradigm language which lets me write more expressive code</li><li>If the project has critical requirements about security</li><li>If the project has critical requirements about performance</li><li>If the project targets many operating systems and I want a truly multiplatform codebase</li></ul><p>There are some details from both of the languages that still triggers me.</p><ul><li>Go focus <strong>so much</strong> on being simple that it has the opposite effect sometimes (like GOROOT and GOPATH, for example).</li><li>I still don’t understand very well how lifetimes work in Rust, and it can get quite frustrating if you ever try to deal with it.</li></ul><p>From a personal perspective, both were very fun to learn, and are a great addition in a world of C and C++. They provide a broader range of applications, like web services and even <a href="https://github.com/yewstack/yew">front-end web frameworks</a>, thanks to WebAssembly :)</p><p>If you want another comparison between the two languages that is far in-depth than this one, check out <a href="https://fasterthanli.me/articles/i-want-off-mr-golangs-wild-ride">this article</a> from <a href="https://twitter.com/fasterthanlime">fasterthanlime</a>.</p><p><em>Originally published at </em><a href="https://cuchi.me/posts/go-vs-rust"><em>https://cuchi.me/posts/go-vs-rust</em></a><em>.</em></p><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=231accadcc88" width="1" height="1" alt=""><hr><p><a href="https://medium.com/magrathealabs/go-vs-rust-writing-a-cli-tool-231accadcc88">Go vs Rust: Writing a CLI tool</a> was originally published in <a href="https://medium.com/magrathealabs">Magrathea</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>