We're choosing Rust, and not Go, C++, or Node.js
symless.com
symless.com
Dev time getting a 3/5 in Go is also quite weird, considering both that Go is a simpler language as well as the fact that it compiles quickly, so whenever you have an issue changing the code to get more info and recompiling is literally a matter of seconds. Besides, most of my dev time isn't really spent in debugging; it's in the architecture. In making things be readable and work well together.
And I'm not even sure what they mean by “Memory Safety”. Access outside of a slice's boundaries causes a runtime panic, not a memory corruption or a segfault as in C++.
As someone who writes a lot of production Rust code, I also find some of the ratings a bit surprising. Go is usually pretty stable in production, certainly better than 3/5.
But at the same time, I am so over catching null-reference crashes in the "QA Phase." Subjectively, it just feels like shoddy craftwork, the equivalent of making wooden cabinets with doors that don't quite close correctly. We can do better than this, and we should.
I have needed to debug major Go packages in production, including Hashicorp products and Pachyderm. And null-reference issues do come up, along with similar sloppiness about data types and invalid states. It's not super-common, but it does happen, and it's a lousy way to spend a weekend emergency.
I'm really hoping that Go continues to evolve in this area. There's a lot that I like about Go, and there's some terrific software written in Go.
> And I'm not even sure what they mean by “Memory Safety”. Access outside of a slice's boundaries causes a runtime panic, not a memory corruption or a segfault as in C++.
I've spent some time fuzzing Rust code using the excellent 'cargo fuzz'. And this made me feel like Rust provides 3 major kinds of memory safety:
1. Bounds checks! This is ancient technology shared by many languages, and it's far less 'cool' than the borrow checker. Nonetheless, it seems to be the single biggest thing that turns serious attacks into controlled shutdowns.
2. Borrow-checking stuff: Use-after-free, etc. GCed languages all have solid solutions for this. Rust's only advantage here is that it provides similar safety without GC overhead (and the poor tail latency and memory usage of many GCed languages).
3. Easier concurrency. This is the one that's easiest to forget. Many GCed languages allow shared data structures to be mutated freely by multiple threads, even when doing so will corrupt data. This usually can't be turned into C-style code injection attacks, but it can cause major headaches.
The only real safety advantage Rust has over GCed languages here is (3). Whether or not this matters depends on exactly how a given program uses threads.
Re. cargo fuzz, Go is getting first-class fuzzing in Go 1.18, which should be out in just a few days, and I'm very excited about all the new null pointer dereferences it finds in my code, heh.
Stability Rust 5/5
Stability Go 3/5
The Recruitment points is even more biased, given the reality where you probably will find close to 50x more applicants familiar with Go versus Rust.
I don't think they're wrong for picking Rust. I really like Rust and it's probably a great fit for them. There's a good chance I'd have picked Rust for the same project. Based on their criteria, I'd probably see it as a bit more of a coin toss between Rust and Go though and I suspect they just picked the one they wanted. And that's totally fine.
> Code written in Node.js and Go could be made memory-safe with enough work, but they're both GC based, which inevitably leads to engineers easily missing memory safety issues
And here I was (maybe naively) thinking that a garbage collector ensures memory safety? Unless they mean memory leaks (of the kind where memory is allocated and can't be freed by the GC, but is also not used?!). But that should be relatively easy to catch with a profiler? Maybe some more explanation would have been in order here...
More generally, glad to see Go got 80% compared to Rust's 90% - just hoping they weighed their priorities correctly, otherwise who knows, with such a small difference Go might have come out on top...
I feel like the body of the post could have been "we just want to use Rust, ok??!"
You can still have use-after-free bugs in a GCed language. A trivial example is calling methods on a closed/disposed/??? socket, file, or other handle. They don't result in the arbitrary memory corruption I'd typically consider a "real" memory safety issue, but they do result in bugs. In some cases, debug allocators and tools like address sanitizer can even surface such mistakes more quickly than similar code in a GCed languages. (Of course, in other cases, mistakes with non-GC memory cause bugs that are an absolute nightmare to repro and debug.)
More troubling is native interop, which I'd assume something like Synergy will definitely need to do plenty of to capture/forward keyboard/mouse input, bypassing the traditional window focus reliant input stack that the language may pre-provide safe bindings for. Failure to pin, or unpinning too early during an asyncronous operation, can cause your GC-allocated memory to shift around during GC compaction when native code was expecting it to remain in one place. The GC can also potentially outright reap GC-allocated objects that native code was using. I remember reading of nasty heisenbugs from Ruby C extensions forgetting to use RB_GC_GUARD to ensure VALUEs remain on the stack, where the GC can find them, when scanning for GC roots that keep objects alive - as an example.
Well, you can write similar bugs in non-GCed languages, but GCed languages aren't immune from these concerns. I've even bluescreened fairly modern Windows, with C# code, by sufficiently misusing SlimDX!
EDIT: links to the aforementioned reading:
What prevents you from doing such things in Rust?
use std::io::{self, *};
fn main() -> io::Result<()> {
let mut f = std::fs::File::create("asdf.txt")?;
writeln!(f, "asdf")?;
drop(f); // closes asdf.txt
writeln!(f, "asdf")?; // error[E0382]: borrow of moved value: `f`
Ok(())
}
Full diagnostics: error[E0382]: borrow of moved value: `f`
--> src/main.rs:7:5
|
4 | let mut f = std::fs::File::create("asdf")?;
| ----- move occurs because `f` has type `File`, which does not implement the `Copy` trait
5 | writeln!(f, "asdf")?;
6 | drop(f);
| - value moved here
7 | writeln!(f, "asdf")?;
| ^^^^^^^^^^^^^^^^^^^ value borrowed here after move
|
= note: this error originates in the macro `writeln` (in Nightly builds, run with -Z macro-backtrace for more info)
For more information about this error, try `rustc --explain E0382`.
Of course, a socket timing out or a drive getting unmounted could still yank a handle out from under these APIs, and e.g. C++ still allows for using such zombie objects. There are also ways to help catch such bugs in GCed languages as well (be it ObjectDisposedException making such bugs shallower in C#, or using lambdas/blocks for scopes to turn continued object use into a syntax error:) # Ruby
File.open("asdf.txt", "w") do |f|
f.write("asdf")
end
f.write("asdf") # f wasn't in scope
Ruby's dynamic nature means this will still fail at runtime, but similar patterns in C# can force compile time syntax errors for use after close.EDIT: You even see this pattern in Rust, occasionally, to limit reference lifetimes to avoid soundness issues (e.g. all the annoying `.with(...)` blocks you see when using thread_local! : https://doc.rust-lang.org/std/thread/struct.LocalKey.html ).
> Stability: How stable the Synergy 3 service is likely to be (i.e. memory safety, etc).
Huh. So, memory safety. And of course, the ever relevant "etc." Well, he did say it was going to be a technical article, very glad I strapped myself in.
> Code written in Node.js and Go could be made memory-safe with enough work, but they're both GC based, which inevitably leads to engineers easily missing memory safety issues
Um. https://doc.rust-lang.org/nomicon/leaking.html
I wasn't aware that Go programs were known for leaking memory due to their dastardly GC.
For shame GC languages that were explicitly designed to prevent a multitude of memory related bugs! For shame!
...has anyone told this gentleman about unsafe blocks?
> "The formula and weighting are somewhat arbitrary"
You don't say... the entire bloody justification is!
----
Meanwhile we can do better than "every C program is a valid C++ program, so C++ can’t lose" defensiveness.
Look at the data tables published with that 2017 paper:
https://sites.google.com/view/energy-efficiency-languages/re...
There's an order of magnitude difference between the times of the selected C and C++ programs, for one thing — regex-redux. That is enough to explain all of the difference between the C and C++ time measurements shown in "Table 4. Normalized global results". (Seems like an outlier which could have been excluded.)
Somehow I don't think choosing a different fancy-language-of-this-week is going to help improve the chances of this new rewrite.
I am a paying customer of Synergy and kindly ask them to focus on their existing software and to Please. Stop. Rewriting. Software. Every. Other. Day.
> But, existing Rust developers of course have a different view.
"I find rust far easier than C++ to work with."
I bet it's far easier than C++ - then again, almost every programming language on this planet is far easier than C++...
I suspect these decisions are far more emotional than most of us here would like to admit.
SNOBOL4 on the other hand, was probably not considered at all.
The whole article is very weird, and there is little to learn from it. Wind on till they've actually made the port, and have run it for some time and then there will be some interesting lessons learnt, gazing into my crystal ball it will be somethong like 'turned out to be harder and take longer than we thought, but the result is good'
You cannot sell C/Java.
Computers are now reaching the maturity lightbulbs had when they artificially limited the burn time to 1000 hours.
Personally I going back to C and JavaSE/J2ME on Rasperry 4/Pico.
(Although being fair, I've met some Java libs that leak memory (looking at you, Couchbase), usually via poor usage of byte buffers. But Rust doesn't let you use unsafe code constructs, so it'll be fine. I guess I need another /s now.)
That "study" has a lot to answer for!