a (negative) review of rust

rust vs ruby
rust is a very "safe" language, and there are cool papers that prove it. as a consequence, I'm sure it's very good for group development and so on. if you're someone like me without industry experience <insert trademark sign>, that doesn't really matter too much. what matters a lot to me is how fun a language is -- and among the many blogposts about rust, there aren't many that assess this quality (and if there are, the author convinces themself that they think good performance is their idea of "fun", or something like that). so how fun (subjectively) is rust? one of the languages that rusts cites as an influence is ruby, a language that was explicitly designed to be fun. overall, ruby's philosophy focuses on more soft, human ideas of usability than formal proofs of "correctness". the main (only?) influence ruby had on rust has to do with rust's closure syntax:
	  let my_closure = |i| { i + 1 };
	
this looks close enough to ruby's idea of blocks:
	  my_closure = Proc.new {|i| i + 1 }
	
that's the extent of the ruby influence though, unless you count the snake_case. unfortunately, rust forgot to `use fun`. ruby's syntax is closely tied to its philosophy, so the claim that rust was actually influenced by ruby is kind of tenuous. programming in rust feels a lot like pulling teeth, and knowing that your program won't unexpectedly crash as often doesn't make that feel any better for a solo programmer who's just working on a hobby project. I should mention at this point that I'm not claiming rust owes me anything, because I know the language was designed for bigger team projects where reliability is crucial. really, this blogpost is an exploration of the factors that affect the experience of using a language. I know lots of people like rust, and the crab mascot is pretty cute. once again this is just an exploration, not an attack.

tangent about design
anyway, I have lots of questions and ideas about what makes a programming language good and bad. as far as the criterion of fun goes, one determining factor (I think?) is whether or not the language is designed by a committee rather than by a "benevolent dictator for life." examples of the former would be c, rust, c++, golang, and java; examples of the latter inclue ruby (matz), clojure (rich hickey), and perl (larry wall). while there's obviously issues with giving individuals credit for programming languages that they invented but that ultimately grew due to the contributions of the open-source community, a committee cannot give a language style. a unified taste or "feel" is best accomplished through the lens of a single person rather than a group that tries to anticipate the desires of an imaginary audience.

but _why_ don't you think rust is fun???
all of this hatred is still kind of vague, though. why in specific do I dislike rust? it will be hard to pinpoint exactly what about it makes me feel bad, because humans haven't had programming languages for most of history -- so there aren't ways of describing how a using language feels the way there are words to describe smells, tastes, or even more established experiences such as riding a sailboat. basically, I'm still waiting for a cassettepunk utopia to be established.

syntax is everything
here's a rust version of the classic 'hello world':
	fn main() {
	  println!("Hello world!");
	}
	
this isn't that bad. compare it to other systems programming languages: haskell
	main :: IO ()
	main = putStrLn "Hello world!"
	
java (*scream*)
	public class Hello {
	  public static void main(String[] args) {
	    System.out.println("Hello world!");
	  }
	}
	
good old c
	#include <stdio.h>

	int main() {
	  printf("Hello world!\n");
	  return 0;
	}
	
the rust 'hello world' has possibly the least amount of syntactic noise out of these examples, although this comes at the cost of having to infer things like types and visibility (although that's not necessarily bad). the thing is, rust programs get a lot more complicated than that. in a language like java, simple programs can look really verbose, but that's just about as verbose as even the really complicated programs end up. this is partially because java doesn't have advanced features like rust or haskell. however, haskell still manages to deal with advanced things like types far better than rust does. here's an example from the Control.Lens package, which is gigantic and allows for some wild functional metaprogramming (comments have been stripped out):
	class
	  ( Choice p, Corepresentable p, Comonad (Corep p), Traversable (Corep p)
	  , Strong p, Representable p, Monad (Rep p), MonadFix (Rep p), Distributive (Rep p)
	  , Costrong p, ArrowLoop p, ArrowApply p, ArrowChoice p, Closed p
	  ) => Conjoined p where

	  distrib :: Functor f => p a b -> p (f a) (f b)
	  distrib = tabulate . collect . sieve
	  {-# INLINE distrib #-}
	
	  conjoined :: ((p ~ (->)) => q (a -> b) r) -> q (p a b) r -> q (p a b) r
	  conjoined _ r = r
	  {-# INLINE conjoined #-}
	
the types themselves are a little cryptic without context, and the typeclass constraints are a bit long. but overall, there's not much visual noise going on that distracts from the main point: a typeclass is being defined, it has lots of dependencies, and the functions are just composed of a few other functions and they get inlined. what's also important is that the big types and type dependencies don't accumulate and get carried with every time you use a `Conjoined p`, because said types can filled in and reduced. a good way to compare this to rust is thinking about flat versus nested syntax, where haskell has pretty flat types and rust's types often nest a lot more.

oh no
next, let's look at some complicated rust code. how bad can it be, right? rust is a modern programming language, and it even uses type inference, so a complex program should look like the haskell example ... right? we'll be looking at tokio, a crate whose description ("A runtime for writing reliable, asynchronous, and slim applications with the Rust programming language") is like the motto of some technology company that doesn't get anything done and is actually a front for money laundering. without further ado:
	impl<T: AsRef<[u8]> + Unpin> AsyncSeek for io::Cursor<T> {
	    fn start_seek(
	        mut self: Pin<&mut Self>,
	        _: &mut Context<'_>,
	        pos: SeekFrom,
	    ) -> Poll<io::Result<()>> {
	        Poll::Ready(io::Seek::seek(&mut *self, pos).map(drop))
	    }

	    fn poll_complete(self: Pin<&mut Self>, _: &mut Context<'_>)
	      -> Poll<io::Result<u64>> {
	        Poll::Ready(Ok(self.get_mut().position()))
	    }
	}
	
welcome to chaos! this would make some pretty cool ascii art, but as code there's so much visual noise. much of what makes writing code fun gets ruined in rust when you have to explicitly write out so many type constraints and memory constraints that it feels like you're trying to write out a legal agreement with a demon who's very intent on finding a loophole that allows your soul to be stolen. if you've ever used rust, you may have had the sad experience of adding a field of type `&str` to a struct that's widely used throughout your codebase, only to spend the next five hours putting a little `<'a>` everywhere. furthermore, you end up getting really weird shapes of code nesting since rust uses parentheses for function invocations and enum construction, and then it also uses method chaining a lot of the time. essentially, it's trying to pull off functional programming tricks but still dressing itself like c or java rather than lisp or haskell or ocaml. and thirdly: how much of this keysmash-esque information is actually necessary in two one-liner functions? rust may be good at eliminating boilerplate functionality, but it only worsens boilerplate syntax. as we have seen, a rust program can grow into a monstrous syntactic fractal that interferes with comprehension. even the "glue" elements of the syntax -- braces, brackets, arrows, etc -- tag along on this fractal train, and they get everywhere. first they're like little warts, and then they explode like popcorn kernels when the program gets even bigger. eventually the fractal starfishy arms of trait/lifetime/borrow constraints grow so out-of-control that these barely meaningful syntactical bits become like teeny tiny bits of glittery sand that stick to the tear-covered surfaces of the annotations, now curling in alien nautiloid shapes across the sky. the abstractions unfurl strange fronds, and a primal instinct inside you tells you to run away -- is it filter-feeding? not-quite-organic processes alight within these structures, yet all that crustaceous glitter obscures it from your sight. there's so much glitter that it rains down now, falling festively upon the ground like sand. you can run your hands through it, and you quickly give in to that sudden urge to trace out pictures of crabs and other unspeakable entities; then the glitter is deep enough that you can make "snow" angels (ignore the rumbling noise), so long as you lie in a vulnerable position, peripheral vision obscured by the rising tides of sparkles. a few specks of glitter even land in your mouth, but you safely cover your face because they taste like phenolphthalein. -- generally, more complicated syntax discourages the programmer from using features that require such syntax, so while it's certainly easy to make lots of .unwrap() calls, doing the functional programming that the rust developers have taken the pain to implement comes at a cost to your own happiness -- and it shouldn't be that way. I also think rust's borrow checker is no fun, but I don't need to explain that at all.