DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports Events Over 2 million developers have joined DZone. Join Today! Thanks for visiting DZone today,
Edit Profile Manage Email Subscriptions Moderation Admin Console How to Post to DZone Article Submission Guidelines
View Profile
Sign Out
Refcards
Trend Reports
Events
Zones
Culture and Methodologies Agile Career Development Methodologies Team Management
Data Engineering AI/ML Big Data Data Databases IoT
Software Design and Architecture Cloud Architecture Containers Integration Microservices Performance Security
Coding Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones AWS Cloud
by AWS Developer Relations
Culture and Methodologies
Agile Career Development Methodologies Team Management
Data Engineering
AI/ML Big Data Data Databases IoT
Software Design and Architecture
Cloud Architecture Containers Integration Microservices Performance Security
Coding
Frameworks Java JavaScript Languages Tools
Testing, Deployment, and Maintenance
Deployment DevOps and CI/CD Maintenance Monitoring and Observability Testing, Tools, and Frameworks
Partner Zones
AWS Cloud
by AWS Developer Relations
The Latest "Software Integration: The Intersection of APIs, Microservices, and Cloud-Based Systems" Trend Report
Get the report
  1. DZone
  2. Coding
  3. Languages
  4. The Struggle With Rust

The Struggle With Rust

Struggling with Rust? Rust's tendency to make you fight the compiler, in addition to making it difficult to do non-trivial tasks, was too much for one soul.

Oren Eini user avatar by
Oren Eini
·
Jan. 31, 17 · Opinion
Like (6)
Save
Tweet
Share
6.29K Views

Join the DZone community and get the full member experience.

Join For Free

So I spent a few evenings with Rust and managed to do some pretty trivial stuff with it. But then I tried to do something non-trivial (low-level trie that relies on low-level memory manipulations). And after I realized that I just have to fight the language non-stop, I am not going to continue forward with this.

Here is where I gave up:

image

I have a buffer that I want to mutate using pointers. So I allocated a buffer with the intent to use the first few bytes for some header and use the memory directly and efficiently. Unfortunately, I can’t. I need to mutate the buffer in multiple places at the same time (both the trie header and the actual node), but Rust refuses to let me do that because then I’ll have multiple mutable references, which is exactly what I want.

It just feels that there is so much ceremony involved in getting a Rust program to actually compile that there isn’t any time left to do anything else. This post certainly resonated with me strongly.

That is about the language and what it requires. But the environment isn’t really nice either. It starts from the basic: I want to allocate some memory.

Sure, that's easy to do, right?

  • alloc::heap::allocate is only for unstable and might change underneath you.
  • alloc::raw_vec::RawVec, which gives you raw memory directly, is unstable and likely to remain so, even though it is much safer to use than directly allocating memory.

We are talking about allocating memory, in a system-level language, and unless you are jumping through hoops, there is just no way to do that.

I’ll admit that I’m also spoiled in terms of tooling (IDEs, debuggers, etc.), but the Rust environment is pretty much, “Grab a text editor, you’ll have syntax highlighting and maybe something a bit more,” and that is it. I tried three or four different editors, and while some of them, intellij-rust for example, were able to do some code analysis, they weren't able to actually build anything (I think that I needed to install JDE or some such). VS Code could build and run (but not debug), but it marks every single warning. Combined with Rust’s eagerness of warning, it made it very hard to write code. Consider what it feels like to work when everything you code looks like this:

image

No debugger beyond println (and yes, I know about GDB, that ain’t a good debugging experience) is another major issue.

I really want to like Rust, and it has some pretty cool ideas, but the problem is that it is just too hard to actually get something done in any reasonable timeframe.

What is really concerning is that any time that I want to do anything really interesting, you need to either go and find a crate to do it (without any assurances of quality, maintainability, etc.) or you have to use a nightly version or enable various feature flags or use unstable API versions. And you have to do it for anything beyond the most trivial stuff.

The very same trie code that I tried to write in Rust I wrote in one and a half evenings in C++ (including copious searches for the correct modern ways to do something). And it works. It is obvious, and I don’t have to fight the compiler all the time.

Granted, I’ve written in C++ before, and C# (my main language) is similar, but the differences are staggering. It isn’t just the borrow checker, it is the sum of the language features that make it very hard to reason about what the code is doing. I mentioned before that the fact that generics are resolved on usage, which can happen quite a bit further down from the actual declaration, is very confusing. It might have been different if I had been coming from an ML background, but Rust is just too much work for too little gain.

One of my core requirements, the ability to write code and iterate over behavior quickly is blocked because every time that I’m trying to compile, I’m getting weird, complicated errors, and then I need to implement workarounds to make the compiler happy, which introduces complexity into the code for very simple tasks.

Let us take a simple example. I want to cache the result of a DNS lookup. Here is the code:

#![feature(lookup_host)]

use std::collections;

struct DnsLookupCache {
     cache : std::collections::HashMap<Box<String>, Vec<std::net::SocketAddr>>
}

impl DnsLookupCache {
    fn new() -> DnsLookupCache {
         DnsLookupCache {
            cache: std::collections::HashMap::new()
        }
    }

    fn lookup(&mut self, host: &String) -> std::io::Result<&Vec<std::net::SocketAddr>> {
        match self.cache.get(host) {
            Some(cached) => Ok(cached),
            None => {
                let mut hosts = Vec::new();
                for  result in try!(std::net::lookup_host(host)) {
                    hosts.push(result);
                }
                let owned_host = host.clone();
                self.cache.insert(Box::new(owned_host), hosts);
                return self.lookup(host);
            }
        }
    }
}


We’ll ignore the unstable API usage with lookup_host and the fact that it literally took me over an hour to get 30 lines of code out in a shape that I can actually demonstrate the issue.

There is a lot of stuff going on here. We have a cache of boxed strings in the hash map to maintain ownership on them. We look them up, then add a cloned key to the cache because the owner of the host string is the caller, etc. But most importantly, this code is simple, expressive, and wrong. It won’t compile because we have both immutable borrow (on line 17) and a mutable one (on lines 25 and 26).

And yes, I’m aware of the entry API on the HashMap that is meant to deal with this situation. The problem is that all those details, and making the compiler happy in a very simple code path, is adding a lot of friction to the code. To the point where you don’t get anything done other than fight the compiler all the time. It’s annoying, and it doesn’t feel like I’m accomplishing anything.

Rust (programming language)

Published at DZone with permission of Oren Eini, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

Popular on DZone

  • How To Best Use Java Records as DTOs in Spring Boot 3
  • How to Use Buildpacks to Build Java Containers
  • Detecting Network Anomalies Using Apache Spark
  • What Is JavaScript Slice? Practical Examples and Guide

Comments

Partner Resources

X

ABOUT US

  • About DZone
  • Send feedback
  • Careers
  • Sitemap

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 600 Park Offices Drive
  • Suite 300
  • Durham, NC 27709
  • support@dzone.com
  • +1 (919) 678-0300

Let's be friends: