Hacker Read top | best | new | newcomments | leaders | about | bookmarklet login

> Not All Programming is Systems Programming

Personally I often use Rust for "non systems-programming" tasks, even though I really wish a more suitable language existed.

Rust has plenty of downsides, but hits a particular sweet spot for me that is hard to find elsewhere:

* Expressive, pretty powerful, ML and Haskell inspired type system

* Memory safe. In higher level code you have almost zero justification for `unsafe`, unless you really need a C library.

* Immutable by default. Can feel almost functional, depending on code style.

* Error handling (it's not perfect by any means, but much better than exceptions in my book)

* Very coherent language design. The language has few warts, in part thanks to the young age.

* Great package manager and build system.

* Good tooling in general (compiler errors, formatter, linter, docs generation, ... )

* Library availability is great for certain domains, decent for many.

* Statically compiled. Mostly statically linked. Though I often wish there was an additional interpreter/JIT with a REPL.

* Good performance without much effort.

* Good concurrency/parallelism primitives, especially since async

* Increasingly better IDE support, thanks to the author of this blog post! (rust-analyzer)

So I often accept the downsides of Rust, even for higher level code, because I don't know another language that fits.

My closest alternatives would probably be Go, Haskell or F#. But each don't fit the above list one way or another.



view as:

You might like Scala and/or Kotlin. You listed Go, but Go’s type system is very weak, as is Go’s support for immutability, two problems that Scala and Kotlin don’t share.

Personally I'm not very excited about making Java a hard dependency, or the fact that Scala can't be bootstrapped like most other languages. [0]

[0]: https://bootstrappable.org


Your 2nd point will stop being true very soon. Scala 3 will apparently be coming out sometime this fall (https://www.scala-lang.org/blog/2020/09/15/scala-3-the-commu...), and the compiler will be the Dotty compiler. Dotty is bootstrapped (https://dotty.epfl.ch/blog/2019/05/23/15th-dotty-milestone-r...).

The first point is true, but IMO not a big deal for most applications. Scala native exists, but is very immature. Still, if you’re writing web services, data processing jobs, etc., I don’t see much of an issue with depending on the JVM. Definitely an issue with things like command line tools, or apps with super tight resource requirements, but that’s not the case for a tonne of software.


> Personally I'm not very excited about making Java a hard dependency

Why be concerned about this? Either way you're making a hard dependency on some languages runtime, be it go, rust, or the jvm and I see it sticking around a lot longer than the others.


Maybe you're still tied to their standard library, but technically Rust doesn't have a runtime.

Many languages compile to assembly so that you can distribute them easily. Trying to send a friend a compiled tool is a billion times easier than asking them to install JDK / Node /etc.

I conceptually love Scala, but in practice it has a lot of downsides.

* Tied to the JVM

* You constantly have to use Java libraries. Which means you have to understand the Java ecosystem. Which increases complexity a lot, since that ecosystem is very mature but also often deeply layered, complex and full of historical baggage

* It can be used in so many different ways. Nicer Java on one end. Almost Haskell on the other (with cats etc).

* The flexible syntax and multi-model concept leads to very variable and non-coherent code styles and libraries

Kotlin is also great, but the type system is less powerful. And the same JVM and Java library ecosystem concerns apply. ( I've jokingly heard "we don't talk about Native" multiple times... )


Scala has been my primary professional language for the past 5 years, and I actually don’t have to use Java libs very often anymore, the pure Scala ecosystem has really caught up. When you do have to use them, they’re normally pretty easy to wrap.

Totally agree with the plethora of ways to use it being an issue, though. “Classic Scala” and “let’s write Haskel is Scala” is a real split in the community, and a significant downside. Then you have Akka too, though at least that solves different problems (highly stateful AND highly concurrent, which is a pretty rare mix).


> You constantly have to use Java libraries. Which means you have to understand the Java ecosystem.

That's not so true in my experience. You have to reach for Java libraries in the same cases where you'd have to reach for C libraries in other languages. When you're using a Java library it's usually to do something specific (because using the big Java frameworks makes no sense), so the complex parts of the Java ecosystem don't really affect you.


You constantly have to use Java libraries. Which means you have to understand the Java ecosystem. Which increases complexity a lot, since that ecosystem is very mature but also often deeply layered, complex and full of historical baggage

This problem is bigger in Clojure which has a smaller ecosystem than Scala and is why I don't pick it for writing "real world" applications in it.


At one point in time, I thought Scala would be this, but it very much isn't. It feels bulky and lacking orthogonality to me, and it's tooling leaves a lot to be desired. (Note: this might be true of Rust too once it is as old as Scala.)

Kotlin, though, yep, big fan.


One of C++‘s biggest draws historically is that it’s not just used to write systems software. Rust could fill a similar spot.

What is C++ used for besides systems software?

Office. Photoshop. Mozilla. Before Android (Kotlin/Java), iOS (ObjC/Swift), and Electron (JS), most GUI apps from the 1990s and 2000s were C++. Also most of Google's search backend (later they started mixing in Java microservices).

Games?

> Expressive, pretty powerful, ML and Haskell inspired type system

This is not a reason to use rust. It's like saying "I like Ferraries, because they drive fast", failing to specify WHY you need to drive fast (you usually really don't, unless you are on a race track)

> Memory safe. In higher level code you have almost zero justification for `unsafe`, except you really need a C library.

Java, C#, etc.

> Immutable by default. Can feel almost functional, depending on code style.

Okay, however this isn't a big win in practice. Java has this too with @Immutable and mostly that's enough.

> Very coherent language design. The language has few warts, in part thanks to the young age.

Yes, Rust looks fine. Let's see how it evolves. Coherent language design is however again not a reason to use rust, because it fails to mention WHY you need it and WHY it solves a business problem better than Java, C# or C++.

> Great package manager and build system.

Yeah, pretty much all modern languages have that, so it's not worth to even mention. Rust builds are slow, so there is that.

> Great tooling in general (compiler errors, formatter, linter, docs generation, ... )

Really? Ever used Java or C# tooling?

> Library availability is great in certain domains, ok most.

Compared to what? C++? I don't even think there it's true. When comparing to Java or C# library availability and quality is a joke in Rust.

> Statically compiled (though I often wish there was an additional interpreter/repl). Mostly statically linked.

Yeah... What do you need that for? Again no mention of why that's even useful.

> Good performance without much effort.

Like in Java, C# and C++ you mean?

> Good concurrency/parallelism primitives, especially since async

Async is a paradigm that received a lot of criticism lately. It turns out to be cancerous. Fibers will likely replace it and Java is getting it soon. Otherwise yeah, concurrency is something any modern language should solve and maybe Rust has a head-start here. The whole purpose of Rust is focused around safe multi-threading. However other languages don't sleep. You don't switch your company to Rust just because it does one thing better for a couple of years. Other languages will catch up soon.


You seem to be very convinced that Rusts main competitors are Java and C#. I don't think this is usually true. Rusts main competitors are C and C++. I agree that in most cases where Java and C# are possible options they should be prefered over Rust. There are however many situations where neither Java nor C# are options.

> There are however many situations where neither Java nor C# are options.

Not too many anymore, and the space for these situations shrinks over time, rather quickly.

20 years ago, almost everything was implemented in C or C++, for all platforms. The computers didn’t have extra RAM for the GC overhead.

Web sites were the first to migrate, probably because security. Desktop GUI apps followed.

On mobile, before iPhone was launched in 2007, people used a lot of C or C++ there. PalmOS only supported C, Symbian SDK was C++ based, most Windows Mobile apps were written in C or C++. For the same reason, not enough resources for a GC. We now have gigabytes of memory in these devices and it’s no longer the case, on Android it’s almost 100% Java or Kotlin, on iOS it’s Swift.

This is happening with videogames, see Unity3D. Even low level soft-realtime multimedia is good enough for .NET now, here’s an example where my C# implementation delivers same performance as C implementation of VLC player: https://github.com/Const-me/Vrmac/tree/master/VrmacVideo

Pretty sure in a couple of years once MS improves a few relevant things in their .NET core (they need better constant propagation, and NEON support) we’ll see high-performance numeric stuff following the trajectory. The hardware SIMD support in .NET core 3.1 is a good step in the right direction.


Can you describe what's wrong with haskell, ocaml (which, incidentally, was a major inspiration for rust), or f#? They seem to tick most of the items on your list, though this one I don't understand:

> In higher level code you have almost zero justification for `unsafe`, except you really need a C library.

If you need a c library, you can build a 'trusted' wrapper around it as easily in rust as with any other language.


> Haskell

I adore Haskell, but my personal problems with it:

* Records! Lenses are fine, but a huge complexity increase. I love the recently accepted record syntax RFC though, this will make things a lot nicer.

* I feel like the plethora of (partially incompatible) extensions make the language very complicated and messy. There is no single Haskell. Each file can be GHC Haskell with OverloadedStrings or GADTs or ....

* Library ecosystem: often I didn't find libraries I needed. Or they were abandoned, or had no documentation whatsoever, or used some fancy dependency I didn't understand. Or all of the above...

* Complexity. I can deal with monads, but some parts of the ecosystem get much more type-theory heavy than that. Rust is close enough to common programming idioms that most of it can be understood fairly quickly

* Build system ( Cabal, Stackage, Nix packages, ... ? ), tooling (IDE support etc)

> F#

I admittedly haven't tried F# since .net core. I just remember it being very Windows-centric and closely tied to parts of the C# ecosystem, which brings similar concerns as in my sibling comments about Java.

> if you need a c library, you can build a 'trusted' wrapper around it as easily in rust as with any other language.

Sure, but if that wrapper does not exist, you have to build it yourself. I can say from experience that writing an idiomatic, safe Rust wrapper for a C library is far from trivial, so you lose the "I don't have to worry about memory unsafety" property.


> I admittedly haven't tried F# since .net core. I just remember it being very Windows-centric and closely tied to parts of the C# ecosystem

FWIW I did a small project recently in f#, using .net core (on linux), and it never seemed like a second-class citizen. Can't speak to the c# ecosystem, though.

> > if you need a c library, you can build a 'trusted' wrapper around it as easily in rust as with any other language.

> Sure, but if that wrapper does not exist, you have to build it yourself. I can say from experience that writing an idiomatic, safe Rust wrapper for a C library is far from trivial, so you lose the "I don't have to worry about memory unsafety" property.

Right. My question is, why is this a mark in favour of rust? It doesn't seem to distinguish it at all from the other languages you mention.


> I feel like the plethora of (partially incompatible) extensions make the language very complicated and messy. There is no single Haskell. Each file can be GHC Haskell with OverloadedStrings or GADTs or ....

Aren't macros, especially proc macros these days in Rust having the same effect? Personally I feel like this is a tradeoff every language has to play with: you either limit to a special way of writing, or adding some sort of ad-hoc system that enables rewriting syntax and even to a degree, semantics.


Anecdotally, proc macros just aren't that common. Almost every Haskell tutorial I read introduces language extensions, and it seems like many users have a set of extensions that they always enable by default. I don't think proc macros are really comparable in that sense, although maybe they will be in the future?

Overly powerful proc-macros aren't in common use; most common proc-macros are either ones that automatically Derive a trait, or ones that serves as attributes on methods or functions and perform some transformation of the source code (without introducing a DSL)

Did you try Swift? It is conceptually and syntax wise very similar to Rust.

Swift does indeed hit a lot of the same sweet points as Rust whilst having automatic memory management and generally more user friendly defaults (at the cost of performance). Alas, it doesn't have anywhere near the library ecosystem that Rust has in most domains, and it's cross-platform support is also quite poor.

I'm a Swift programmer. I've also used plenty of other languages. I don't know enough about Rust to either sing its praises, or curse its name.

I'm a bit leery to label Swift as a "systems language," as I feel that systems languages should be a wee bit closer to the bone (like ObjC); but that also means that I am not a fan of using systems languages to write application code. I wrote both levels in C and C++ for years, and HATED using those languages for app-level code. Swift, to me, is an almost ideal application language.

But I don't write system code anymore, and have no desire to. I love writing app-level code, and love Swift.

It's been a long time since I've had the pleasure of employing a language I actually like using.


I did, and I think it's a great language in many ways.

My biggest problem is that, as far as I know, it still is very much second/third class citizen on non-Apple platforms. With little signs from Apple that this will change. (Happy to be corrected here!)

Other (more minor) complaints are the Objective-C baggage and the inheritance system - I enjoy the lack of inheritance in Rust.


My understanding is as soon as you target on a non-Apple device, you lose a large chunk of the API (foundation). This includes things like file system interaction, date handling, sorting/filtering, networking and text processing.

My understanding is there is some effort to provide a version that works outside of Apple OSes, but it’s incomplete https://github.com/apple/swift-corelibs-foundation/blob/mast....

If you are looking for static linking stdlib it looks like there’s still an open bug on Linux. https://bugs.swift.org/plugins/servlet/mobile#issue/SR-648


FWIW, as an iOS-centric programmer who’s also implemented a bunch of scripts and several moderate API projects in Swift/Linux, I haven’t found the OSS Foundation to be particularly lacking. The only quirk I can remember having to work around is the fact that for some reason, all the networking code (i.e., URLSession) is pulled out into a separate module, FoundationNetworking.

FWIW, looking through that list you linked to, it seems most things are complete apart from relative edge cases (NSGeometry), fairly Apple-specific (Bundle), or replaced by something newer (NSCoder). The big one that jumps out to me as being a larger issue is the URL authentication stuff… so I guess steer clear if you need to handle basic auth/Kerberos/etc. The XML parsing being mostly incomplete is a bummer, too. :(

Ultimately, though, I’ve found Swift on Linux to have everything I need for what I’ve been building with it. Obviously, YMMV, but (and not just for you, anyone else reading as well), I wouldn’t be scared off just because Foundation isn’t 100% complete.

###

Edit: that being said, this is also just a defense/rebuttal to the comment I’m replying to. Swift isn’t the answer to all projects; I still reach for Clojure as my first choice for large API projects, mostly due to my own preferences.


It’s been a while since I had checked, so I’m glad to see it’s filled out a lot more. It seems like the language evolution has slowed down a bit too. The last time I had given it was a little after the swift 3 release which had quite a lot of breaking changes. I let my previous experience here prevent me looking deep enough into it.

I’ll have to give it another shot.


What kind of programming do you do? I feel like this is important context for understanding your perspective.

> Expressive, pretty powerful, ML and Haskell inspired type system

It's great that they use this, but it's still difficult to program in a purely functional style in Rust the way you would in, say, Haskell, because of memory management. Closures can create memory dependencies which are too difficult to manage with Rust's static tools.


This is definitely true, but you can often get surprisingly far by just boxing, cloning, Rc-ing, etc. whenever you hit something like this.

Yes, but doesn't the additional housekeeping negate much of the elegance of Haskell?

the bigger issue is around the combination of closures, generic parameters, and traits. because higher-rank types can't be expressed and closures are all `impl Fn()`, you start to get an explosion in the complexity of managing the types and implementing the trait. try implementing something in the "finally tagless" style for an example of the kinds of things that can go wrong really fast. `Box` and `Rc` don't get you out of it and the resulting implementation is brittle and boiler-plate heavy in actual use.

from memory:

    trait Prop<'a, T> {
        fn p<F: Fn(&'a T) -> bool>(f: F) -> Self;
        fn eval(&self, &'a T) -> bool;
    }

    enum LiftedBool<'a, T, F: Fn(&'a T) -> bool> {
        Base(Box<F>),
        And(LiftedBool<'a, T, F>, LiftedBool<'a, T, F>),
        Or(LiftedBool<'a, T, F>, LiftedBool<'a, T, F>),
        Not(LiftedBool<'a, T, F>),
    }

    impl<'a, T, F: Fn(&'a T) -> bool> Prop<'a, T> for LiftedBool<'a, T, F> {
        fn p<F: Fn(&'a T) -> bool>(f: F) -> Self {
            // this fails because `F` in the impl doesn't necessarily unify with `F` in the trait definition
            // it can be made to work with `dyn` for the function argument but requires boilerplate to actually use
        }

        fn eval(&self, &'a T) -> bool {
            // obvious evaluation by cases
        }
    }
this example can be fixed to work via `dyn` for the argument on the trait function and the struct itself but it becomes increasingly painful to actually work with. this kind of generic work is not at all ergonomic but very easy in Haskell.

True. My biggest complaint there is actually lack of guaranteed tail call optimization.

An interesting development in Haskell is Linear Types. It opens the possibility of non-garbage collected Haskell. Still a lot of work left but it’s based on theory similar to that behind the Rust borrower mechanism (if I understand it correctly).

https://www.tweag.io/blog/2020-06-19-linear-types-merged/


A "non-GC Haskell" would also need some way to make strict and lazy evaluation equally idiomatic and freely interchangeable in the language. Some general approaches are known that would clearly help with this issue e.g. "polarity" and "focusing", but the actual work of designing such a language has not been done.

I feel like it should be trivial to make a 'scripting' variant of rust. Just by automatically wrapping values in Box/Rc when needed a lot of the cognitive overhead of writing Rust could be avoided. Add a repl to that and you have a highly productive and performant language, with the added benefit that you can always drop down to the real Rust backbone when fine-grained control is needed.

Check out Rune: https://rune-rs.github.io/

It’s more of a prototype, but I think it’s in the direction you’re describing.


Similiar to GP, I too have been wondering about a Rc'd Rust.

Unfortunately Rune and Dyon[0] are dynamically typed, which isn't so attractive to me.

More promising are Gluon and Mun, both of which are statically typed. Of these two, Gluon has a somewhat alien syntax if you're coming from Rust (it notes it's inspired by Lua, OCaml and Haskell) so Mun is probably a better choice...but it seems very early, and the website notes as much (to serve the needs of a Rust-scripting-language I'd want seamless interop between it and Rust, which isn't quite there).

So I don't think there's anything in this space right now, but there are some promising options.

If you're wiling to go a little further afield, I'm kind interested in assemblyscript[3] - it's 'just' another WASM-based language so it's not a huge leap of imagination to believe there could be tooling to enable the seamless Rust interop. Just a matter of effort!

[0] https://github.com/PistonDevelopers/dyon [1] https://github.com/gluon-lang/gluon [2] https://github.com/mun-lang/mun [3] https://www.assemblyscript.org/


gluon looks great, but the book site is down for some reason, which is unfortunate since i am looking for something nearly exactly like this.

Why not D? It's suitable for system programming and much better than Rust for non-system programming due to the default GC (but you can disable that where appropriate).

It run fast due to native compilation and program compilation time is much faster than its competitors including Rust and C++.

D also now testing memory ownership support inspired by Cyclone/Rust.

It can interface easily with C, C++ (via DPP), Python and R [1].

[1]https://dlang.org/blog/2020/01/27/d-for-data-science-calling...


I just wish they had more manpower and a straight idea of what D should target as roadmap.

Legal | privacy