Hacker News new | ask | show | jobs
by randomThoughts9 3154 days ago
Well, a fun fact: there is nothing funny about that list :). It should have been called "Some facts about Rust's growing popularity".

Now, my predicament: I love Rust's type system and tooling, but it's really hard to justify to myself the pain of writing correct Rust code (borrowing, lifetimes, etc.) when I know I can get almost the same effect by using a GC language + immutable messages.

And I don't need that last drop of performance either.

So if you just want a native language with a good type system and a growing ecosystem, where do you go? Still Rust or something else?

12 comments

You're overlooking that Rusts "solution"(borrowing, livetimes, etc. ) is not only solving the same problem that garbage collection tries to solve. Garbage collection is "only" solving the "memory resource problem". Rusts solution is a more general solution to the "general resource problem" .. like file handles, sockets and of course memory and besides that gives you tools to never get bitten by data races. Nothing (besides memory) garbage collection is helping you with. You have to mitigate those problems still in garbage collected languages like Swift, Go, Java, Python (to name a few) and various other solutions to help with this (try with resource, with statement, immutable message passing etc.) Rust tries to give you one solution "to rule them all". Is this better, worse? Idk – nobody does, the Rust people are trying to figure this out :) Nothing is carved in stone. And if you're more productive in another language you should use it! But you can only know by figuring this out for yourself. Rust works well for me – that's no guarantee that you have the same experience. Rust is still hard to learn and one needs to question himself if the effort in learning pays out at the end – it has for me, i guess/hope ;)
>Rusts solution is a more general solution to the "general resource problem"

It's true. Ever since I got familiar with the ownership concept, I've taken to writing C# IDisposables with disposable member variables like this:

   public MyObject(){
      this.myResource = new DisposableResource();
      this.ownsMyResource = true;
   }

   public MyObject(DisposableResource resourceToUseThatIDontOwn){
      this.myResource = resourceToUseThatIDontOwn;
      this.ownsMyResource = false;
   }

   public void Dispose(){
      //not pictured:  .NET's boilerplate dispose code

      if(ownsMyResource) myResource.Dispose();
   }

For the client I'm contracting with right now, mismanagement of disposable resources is the #1 issue in the codebase. 100k lines of code, and it's never clear who should be disposing connections. There are some objects with multiple constructors (like above) that have 'conditional' ownership. In Rust, it's impossible to have this problem, period...although the above C# construct simulates it, lol.
Fun fact: what you did is a hand implementation of Rust's "drop flag" (https://doc.rust-lang.org/nomicon/drop-flags.html).
That's pretty cool - it never occurred to me that determining drops required anything other than static analysis. Nice that it doesn't affect the layout of the types, either...which I'm guessing is why they added:

>The drop flags are tracked on the stack and no longer stashed in types that implement drop.

I agree and I appreciate what they are trying to do.

But just as a counter-argument, look how a simple lock looks like in Rust [1]:

  fn main() {
      let counter = Arc::new(Mutex::new(0));
      let mut handles = vec![];

      for _ in 0..10 {
          let counter = counter.clone();
          let handle = thread::spawn(move || {
              let mut num = counter.lock().unwrap();

              *num += 1;
          });
          handles.push(handle);
      }

      //...
  }
[1] https://doc.rust-lang.org/book/second-edition/ch16-03-shared...

----- edit: Just to clarify my point. The ownership model is a beautiful solution to the resource management problem. And as the parent pointed out, some concepts can be cleanly borrowed/reused in other languages.

But now, in Rust, in practice, you realize that this model requires a lot of boilerplate code just to make the borrow checker happy, even for simple things like using a lock: create Arc(Mutex(data)); clone the arc reference; move the new Arc reference to the new thread.

The issue really is lack of scoped threads forcing usage of reference counting to figure out when to drop a value. With crossbeam scoped threads implementation this looks much nicer.

    extern crate crossbeam;
    
    use std::sync::Mutex;
    
    fn main() {
        let counter = Mutex::new(0);
        crossbeam::scope(|scope| for _ in 0..10 {
            scope.spawn(|| *counter.lock().unwrap() += 1);
        });
        println!("{:?}", counter);
    }
Four lines to declare a mutex, start 10 threads that all lock that mutex, increase value behind mutex, unlock mutex, and join all spawned threads. There is a lot going on here (unwrap is arguably a noise here however, but the rest is straight to the point).

Also, it's possible to use atomic ints here instead of mutex. Verbosity of mutex actually helps a bit, because locking is fairly expensive and it's better to avoid locking if possible.

You can simulate something like that using lambdas in C#.

Something like

    withLock(counter, (num) => {
       return num + 1;
    });
or in alternative,

    withLock(counter, (ref num) => {
       num++;
    });

If C# allowed for trailing lambdas it could look better.
You would still need to have lots of resource management problems to consider using Rust, i.e. it is useful to those high performance/embedded domains that still use C/C++. There are high level dynamic solutions even for things like data races these days (e.g. transacations) if you don’t need to juice every last bit of perf out.
GC helps with file handles. Just give the "file" object a destructor that closes the underlying handle.
... and, it will be eventually closed. Maybe. Definitely before the program quits. Not sure that it'll be closed before you run out of file handles, though.
Rust is not for everything, but for the situations where GC is unsuitable (e.g. you have real-time constraints), and a few others, benefits become obvious (and it's hard to look back once you are there).

Yes it can be a pain to write. But I prefer the pain of making the compiler happy than the pain of debugging a weird segfault.

I suggest taking a look at D[0] optional GC if you need to get that low level. A bit older than Rust.

[0]: https://dlang.org/

From what I've seen so far (just getting started with D), I like D a lot:

* compiled, statically-typed, GC'd

* _very_ fast compilation (this is DMD; haven't tried the other two implementations (one on LLVM, the other on GCC))

* very fast executables

* easy linking with and use of C libraries

* familiar and practical

> [...] but it's really hard to justify to myself the pain of writing correct Rust code (borrowing, lifetimes, etc.) when I know I can get almost the same effect by using a GC language + immutable messages.

> And I don't need that last drop of performance either.

You probably write more highlevel code? Rust is --as the name implies-- most suited for close-to-the-metal code. This is often where that "last drop of performance" counts.

> Rust is --as the name implies-- most suited for close-to-the-metal code.

There are a lot of projects that really could be written in a high-level language, but they were started when C was still preferable due to the immaturity of higher-level solutions. I think it is sad that people, looking at these aging and unaudited C codebases, are thinking about rewriting them in Rust, when rewriting them in e.g. Python might make better sense. Very little Free Software needs to be so close to the metal, and it was just a historical accident that we got so much written in C.

Most of the FOSS software originated with the UNIX culture, where C thrives and the original FSF Manifesto suggested using only C to increase portability between POSIX systems.

So the historical accident was UNIX/POSIX taking over the majority of OS architectures.

> I think it is sad that people, looking at these aging and unaudited C codebases, are thinking about rewriting them in Rust, when rewriting them in e.g. Python might make better sense.

I agree... apart from the Python bit. :) When deving software that is very big (LOC) or very widely distributed (FLOSS packages), then I prefer "well typed" languages that do not need a VM/interpreter.

But Rust is linker compatible with C and C++, meaning the typesafe rewrites can also be shoehorned into Python, just like its native extensions in C are...
> Still Rust or something else?

My personal answer to that is OCaml. The only thing I miss there is good concurrency support. I can see how this might open a whole new can of worms. Would we still use locks, or some addition to the type system like Pony's reference capabilities?

I need that last drop of performance but only for a very small part of my code. For the rest, it's just painful to either have to implement iterators with tons of boilerplate ownership or declare things as sitting boxes on the heap. Both distract a lot from what I'm trying to do which is annoying.

I wish there was a higher level Rust sublanguage where heap allocation and GC was default, and you would use the "bare" Rust for the fast core of your program.

You might look at LuaJIT as that language, with the inner loop in Rust.
It's very easy to do the Java/C, C#/C, C#/Rust, Python/C dance, but there is always a massive impedance mismatch at the boundary, and there are always two different build systems and package managers etc involved.

I'd like to see a language that is both systems and high level at the same time, with trivial interop and a common build system + package manager.

This could e.g be a set of low level extensions added to C# (Span<T>, ref returns etc is getting there) or a high level wrapper language/subset for e.g. Rust.

There is always a best tool for the job, and most jobs might require two tools, so it would be great if those two tools were aware of each other and came in the same toolbox.

Totally agree. The thing with LuaJIT or more appropriately stock Lua is that the VM is small enough to include in your project directly. If Vec and String were exposed to Lua first class construct, we would be a long way there.

The two most mature, well-designed languages in the Rust world are Gluon [1] and Dyon [2]

I believe there is a _great_ opportunity for something like Terra [3] but woven into Rust. Perhaps with the same type of compiler plugin that enabled HolyJIT [4]

The grail is having both a GC and an ownership based gc-free lower level all in the same language. Start dynamic, harden to static and sprinkle in properties.

As a side note, I have been thinking about how to bolt ownership onto a dynamic language and then I learned about Snowflake [5]. I think it would be totally doable to bolt ownership onto Java using annotations and AoT subsets of a program, completely escaping the garbage collector. This would be like Terra++

[1] https://github.com/gluon-lang/gluon

[2] https://github.com/PistonDevelopers/dyon

[3] http://terralang.org/

[4] https://github.com/nbp/holyjit

[5] https://www.microsoft.com/en-us/research/publication/project...

There is also a Lisp dialect: https://github.com/murarth/ketos
To me, Swift feels very similar to Rust only a lot simpler. Arc is also (imho) a nicer solution than a GC (though people have different opinions on that). Even better, with future versions, some of the features of Rust (i.e. lifetimes) will also come to Swift in an opt-in way. It is still a young language but fun to code in.
What's coding in Swift like outside of being on macOS? Is the tooling decent on Ubuntu / *NIX or Windows?
Tooling is all right, not as good as the cross platform tooling for something like Java, but still pretty good, especially given how recently Swift has had any non-hobbyist presence on non-Mac platforms.

The core language "just works" pretty pleasantly on other *nixes, I don't know about windows.

You end up missing Apple-specific libraries a lot, though. There are some packages making progress on that problem, but a comprehensive replacement for e.g. audio or graphics surfaces is a ways off, I think, if only because of the fragmentation that exists in those spaces on other platforms. Of course, Swift has that problem only a little worse than any other language that ships without an OS.

AFAIK there is no official Swift support for Windows, you can use it under WSL though if you want.

On Linux there is official support for Swift.

It's really coming along now with Swift 4, but it is still (of course) behind the Apple platforms.

The "core language" pretty much works great on Ubuttnu, but there are still minor annoyances getting it installed, or building it, on Linux. Other Linux targets, also doable, but more annoying. Windows, I haven't really heard of people doing for real. Not sure that's even on anybody's radar.

What's really awesome is how far Swift has come with the core lib Foundation, which is a from-scratch clone of Apple's Objective-C and closed-source macOS/iOS Foundation library[1], written in Swift, in the open. It is not done, but huge swaths of it are now done[2], and that makes coding in Swift on Linux a lot more pleasant, since we don't typically want to waste time rolling our own regex support, Unicode string manipulations, basic geometry routines, HTTP networking, date formatting, etc. But Foundation is a huge, almost 30-year-old library that has evolved continuously and has shipped with every OS X/macOS/iOS release. An open-source Swift re-implementation of that sounded like "yeah right" bullshit/fantasy when they announced it but now it looks very real just a couple years later.

Tooling is different issue, though. Swift is a modern language with excellent support for auto-completion and in-editor hinting and help. People complain about it all the time, but Xcode is so much better than any IDE on Linux (or any other platform) for coding in Swift that I do most of my Swift-on-Linux work using a Mac, with Ubuttnu running in VMWare Fusion. If a basic editor will do, though, you have a lot of choices. For builds and package management, Swift Package Manager is now built into Swift, and works great. (It also has an option to automatically generate Xcode project files from Swift packages, which is hugely useful; my practice, and I think the prevailing or at least emerging convention, is to keep the package and source in git but treat the Xcode project as a throwaway item, not in version control, that can be generated whenever needed. So you don't need Xcode, but its easy to use for editing convenience when you want, assuming you have a Mac. But not all devs need to have Macs.)

I think you still have to like Swift a lot to choose it for your Linux project, but it is doable. I'd be surprised if Swift adoption on Linux didn't quintuple or sextuple by the end of 2018.

[1]: https://github.com/apple/swift-corelibs-foundation

[2]: https://github.com/apple/swift-corelibs-foundation/blob/mast...

ARC is nicer than GC and more predictable but in terms of performance for stuff like audio raw graphics processing and so on you still want to use something more low level like Rust, C or C++.

One of the reasons iOS is more smooth than Android there's no "OK I'm going to GC a million objects right in the middle of your scroll" performance dip but it will release all objects more gradually through ARC at a slightly bigger total performance cost.

Ask C devs why they (still) think Swift is a PITA to work with compared to Objective-C. Luckily in most scenarios you can use Objective-C mixed with Swift to get the advantages of both scenarios.

> So if you just want a native language with a good type system and a growing ecosystem, where do you go? Still Rust or something else?

Haskell?

Or on today's HN: https://news.ycombinator.com/item?id=15582429

> So if you just want a native language with a good type system and a growing ecosystem, where do you go? Still Rust or something else?

https://www.youtube.com/watch?v=zt0OQb1DBko

> And I don't need that last drop of performance either.

If Nim could mature a bit it would be really nice. There is OCaml but it is a bit goofy to work with if you prefer more imperative style coding. There is Go but no generics.

> So if you just want a native language with a good type system and a growing ecosystem, where do you go? Still Rust or something else?

OCaml is a good choice. The ecosystem is definitely growing, though maybe not very quickly.

> So if you just want a native language with a good type system and a growing ecosystem, where do you go? Still Rust or something else?

Nim[1] is a good alternative to Rust if you don't mind a GC, and I argue that even for use cases where a GC might be problematic it's still a good choice thanks to the Nim GC's soft real-time capabilities.

1 - https://nim-lang.org