
I'm now torn between learning Rust or Go. Any advice greatly appreciated, although this being el Reg I'll probably just get piss taking comments ...
Oh hey, Verity, how's it going? What have you been up to? Busy, busy, busy? This is not a tech podcast. We can lose the opening toe-curl of friendly chat. Look who's a Grumpy Gerty today. I see from your headline that you are extolling Rust? Indeed yes, but please do stop poking your finger through the fourth wall, I've only …
I thought long and hard about Rust or Go, when I was starting a new project that I wanted to be fast.
In the end, I went for Rust; it seems to have fulfilled it's promise to be honest. The code is clean, succinct and even with a naive implementation is more than an order faster than Java equivalent that I wanted to replace (I realise that Java is not a speed monster, here). In terms of usage, the tooling is very nice and comfortable, and the language really gives the impression of being carefully thought out, and as equally carefully developed and updated. Of course, there is only so much time in the world, so how much of this would be true of Go, I cannot say, because I tried it less.
In the end, there were a three main reasons for choosing Rust. First, although it is indeed a fussy language, the community is very supportive and has helped me when ever I needed it (and as probably the first local adopter I needed it). Secondly, it's backed by Mozilla who are, I think, less likely to be evil than google; in particular, there is a strong community ownership of Rust. And, finally, the killer issue, which is reason enough to choose all by itself: Go uses tabs by default; and not just tabs it uses combined tabs and spaces; it is too hideous for words.
Then why not learn both and then decide? In the end, it will come down to personal preference and the job you want to do. But you could do worse than read up on why they were developed and what they've been used for since.
From the outside I've seen more people get more enthusiastic about Rust and the problems it prevents than the things that Go lets them do and Mozilla's use of Rust in Firefox is an excellent way of getting the tyres kicked.
I wouldn't worry too much about "elegance" as it's very subjective. Go has a lot of things going for it but seems to have found its niche in moving and processing enormous amounts of data (time series databases: Google apparently started work on it because they didn't have anything that could help them replicate search data fast and reliably enough. It has support for parallelism and concurrency built-in and compiles quickly for fast executables. But Google has different needs than most of us – not just the scale of the data processed but also the size of the teams working on stuff.
Rust was developed to solve different problems, notably ones associated with memory, which is why you get all the compile errors. And I think this is why it's picked up so many friends so quickly, because despite what anyone says, writing C/C++ code that is really memory-safe is hard™. Rust has also had the chance to profit from things learned in the last few years.
A friend of mine runs a podcast for Rust on YouTube and he does a comparison of the two languages. If you have the chance, go to a local user group for more information.
Mozilla or Google.
I disagree that rust is a C++ killer. Its clearly C-like with a very annoying/helpful compiler.
Mozilla is a C shop.
If you are comfortable with C but dont want to live with that, slightly scary, did I remeber everything feeling before you push, rust is yer man. Unit tests in the same file as the code seems so obvious once you see it.
I dont know enough about Go to offer a comparison: its as hip as Gmail. ;)
Rust is C-like? Well, it's quite a long way from being C. I think it might be a C++ killer - modern C++ is a nice language, but it still doesn't watch your back like Rust does. If universities start teaching it in preference to C++, I'd say the writing was on the wall.
That fussiness in the compiler could do some interesting things for OSes. Strong reassurance of memory correctness without the need for testing, review, debugging? That's got to be a good thing in an operating system. MS are already thinking of using Rust in Windows.
Mozilla *isn't* a C shop and hasn't ever been really. There are some C libraries in Firefox (e.g. NSPR, libxml etc.) but the majority of the browser engine is written in C++. The majority of the front-end (i.e. the user interface and services wrapping the engine) are written in Javascript. Things are bound together with something called XPCOM which is basically a cross-platform version of COM where proxy / stubs & interfaces are derived from interface definitions.
Mozilla is currently developing a new browser engine called Servo and are replacing chunks of the existing engine as that becomes ready. Servo is written in Rust and the hope is that they can make the engine far more concurrent than it is now without worrying about many of the problems that plague concurrent C++.
Swings and roundabouts. I like GO 'cos it has fun things like built in lightweight threading, only one looping statement, multple returns off functions and no concept of classes, it's simple, rigid but powerful simple construct. One reason that RUST interested me, just like GO, no runtimes ( one thing I hate about C# and Java, the baggage of a runtime ), you get compiled code for any of the key x86 platforms with native executables. RUST is a brutal, hands dirty O/S working language which appeals to me for making rock solid O/S utils to get jobs done quickly.
The mascots...GO has a cute, cuddly little gopher mascot but RUST has very the heavy metal "mascot" of rusty circular saw blade like Blackie Lawless from WASP used to have wrapped around his crotch in the 1980s!! Ha ha!
this++
It annoys the hell of me when people plan for using a single language only. There is no single language good for everything, so when you do that you are basically planning for fail. Also, programming languages are not really that different, so making assumption that one can only learn one language well is pretty demeaning to everyone involved.
I have never encountered a suitable situation for the use of a Do/While loop - I've tried using it in the past and then had to fix it back to a regular while.
I ask the audience: Why would you ever use a Do/While? The standard While performs all the same requirements at the cost of one parameter check for the first iteration. It feels like such an obscure language feature that including it in the same sentence as for and while gives it vastly undue time under the spotlight!
/rant
Most looping is iterating over data structures, and nowadays most languages have some kind of ForEach which is a godsend.
For the other type of loops where you're waiting for some condition before you exit, I almost always prefer an infinite loop with an explicit breakout when the condition is hit, rather than the different while/until constructs which force you to move the condition to the beginning or end.
Although the while() construct can perform anything the do..while() can also perform, the semantics have a subtle difference that can convey the intent of the author.
The do..while is guaranteed to enter the conditional at least once and is useful where a process is known to require at least one pass within the conditional.
One of the tasks that was suitable for this was to calculate airline miles (this was how long distance calls were priced from payphones [what are they?] and residential lines in the distant past - I do not know if this still holds).
In general, if the task is guaranteed to require at least one pass, the do..while construct clearly shows that to be the case where 'while' may or may not actually enter the conditional.
Checking convergence on some merit value calculated within the loop. You can of course cheat by initialising the value to force the first iteration, but it always seems a bit inelegant. (And of course you can always loop while true as rust does and break whenever you want, but it leaves me wanting a shower afterwards.)
> Right, but can you give an example of such a loop?
The legendary Duff's device of course.
This post has been deleted by its author
"Why would you ever use a Do/While? The standard While performs all the same requirements at the cost of one parameter check for the first iteration."
For exactly that reason, you know you need to execute the code block at least once, but don't know for sure how many times beyond that. Sure it might be a trivial thing to test that extra time if your condition is a simple one, but if the exit test is a big overhead call you might just choose to use do/while().
I seem to recall, years ago when BBC Micros ruled the world, there was a version of BASIC that had a single LOOP...REPEAT construct to which you could add an optional WHILE or UNTIL on either the LOOP or the REPEAT statement. This struck me at the time as eminently sensible and I've wondered why more languages don't have something similar.
[Edit: and possibly a FOR construct on either end as well.]
Acorn BASIC and Atom BASIC (pre-BBC BASICs from the same company) had DO...UNTIL and FOR...NEXT loops.
Although due to limited memory and the ability to abbreviate keywords on those early micros, they more often had DO... U. and F. ... N. loops (abbreviations worked on the BBC too, but it expanded the tokens in program listings afterwards).
> Why would you ever use a Do/While?
Do/While is expressive if used right: do { move(); } while ( not_at_endpoint() );
Also, if you know the body is done at least once, you save a conditional branch. With a fuckton of short loops this can give a nontrivial speedup. This of course for do {body} while(cond);, not for while(cond) {body}.
Yes, I do HPC, thanks for asking.
> Do/While is expressive if used right: do { move(); } while ( not_at_endpoint() );
To be correctly expressive you would use:
do { move(); } until (at_endpoint)
I have never understood why compiler writers feel it's necessary to choose The One True Way, when english gave us two different words (while, until) for the two different use cases. Personally, I would have both, and let you use them at either end. But I'm kind of slutty.
Actually, since I have known a couple of people who found it necessary to write their own programming language, it is pretty obviously an emergent property of the personality type.
I'm similarly biased toward plain ol' while loops. However, it leads to both an extra check _and_ a seemingly pointless initialization. For example,
int i = 33;
bool solution_found = false;
while( !solution_found && i >= 33)
...
as compared to
int i;
bool solution_found;
do
....
while( !solution_found && i >= 33);
Not a huge difference, I concede. But in either case, some ugliness creeps into your code.
do { ... } while(0) was a tool taught me* in the 1990s for use in C macros.
When you have your code:
if (x) frobulate;
you don't need {} after the if even when frobulate is #define'd as multiple statements of something.
* by the pdksh maintainer Michael R?? er, web search suggests Rendell
I program Rust and it would be nice if it formally supported do-while. But you can actually write a do-while using this slightly weird code:
fn main() {
// Print numbers 0 to 9
let mut i = 0;
while {
println!("i = {}", i);
i += 1;
i != 10
} {}
}
The block after the while evaluates to a boolean expression so it's doing stuff, evaluating the result to true or false and then has an empty block of code.
Please forgive the cluelessness, but isn't do...while the same as repeat...until, just with inverted loop-end logic? If so, is there a reason earlier languages such as Pascal and BASIC had repeat...until constructs before while...wend? Perhaps it's because the underlying code for while...wend requires a convoluted jump out of the loop? Imagine doing it with only BASIC's if...then and goto, or in assembler. I.e. repeat...until is
While...wend is
Which is probably optimised by a compiler into a repeat...until anyway :-)
M.
"Why would you ever use a Do/While?"
Do... While can be a useful construct, for example, in microcontrollers where you're waiting for a piece of hardware to come up with a result and you need a complex set of IO instructions to get to the status register.
Some complex IO stuff to trigger device to do something
DO
{
Some complex IO stuff to get to device status register.
Read device status register to get READY flag.
}
WHILE ( READY == not ready )
of course, you can put the WHILE at the beginning...
READY = not ready
Some complex IO stuff to trigger device to do something
WHILE ( READY == not ready )
{
Some complex IO stuff to get to device status register.
Read device status register to get READY flag.
}
but to me the first version seems to be the more logical, and encapsulates the scope of READY to within the loop instead of extending it outside.
Of course, if the IO device status register is directly accessible without more complex IO operations:
Some stuff to trigger device to do something
WHILE ( IODeviceStatus == not ready )
{
}
would be logical too.
Depends where you want/need your loop termination check, which depends on what you're trying to do in the loop. Do you need to check before the loop iterates its content, or after?
Do you need:
to do something only if a clause permits it (while loop i.e. before), or...
to do something and then check the outcome (do-while loop i.e. after), ?
Do something only if it's permitted:
while( clause == true )
{
clause = DoSomething();
};
Do something and loop on the outcome:
do
{
clause = DoSomething();
}
while( clause == true )
Either form can usually satisfy both requirements with a little excess biolerplate, which makes it a matter of personal preference or enforced coding standards, and how much boilerplate you need to make it happen.
Damn these kids who want to express the intent of the code by using different looping constructs. You will have JMP and you will like it! If you're really, really good Santa might bring you a JNZ for Christmas so you can do conditional branching without having to overwrite opcodes in memory.
Anyone who can not infer the intent of your code from what it does is clearly a lesser programmer, verging on subhuman, who does not deserve to bask in the splendour of your code.
>referenced memory is always valid, memory references can only be written to from one place, inter-thread data races are prevented
I'd like to see how that's achieved *efficiently*. And by efficiently, I mean in the 'language appropriate for system code', 'language as fast and compact as assembly and yet still portable' sense.
But I'm a sceptical old dog, so I got my favourite old bones...
Oh, that's easy. It is all achieved at compile time. So, *efficiency* in the runtime sense of the word is not affected at all. The "portability" bit largely comes from LLVM which Rust uses to produce the final binaries. Or to look at it another way, Rust achieves memory management through it's type system.
The compilation process is a little bit slow, though, partly because of this checking, although, in practice this time is far outweighted by LLVM doing it's thing, especially when running under full optimisation.
The original poster is also simplifying things somewhat; actually, Rust has a "safe" subset that does all of this. It also has an "unsafe" set -- you lose the guarantees, but can do any memory manipulation that you want, including referencing invalid memory locations. You don't need to do it that often, but the option is there if you need it.
I had to look that one up, and then found to my surprise it is more or less the same sort of thing as the old (and discouraged) FORTRAN "arithmetic IF" construct.
Why do folk add stuff like this to the language? An optimising compiler can sort out many if() style constructs, and if it really matters to get greater speed on a high-overhead test you can code it by a simple intermediate variable to be tested twice (and probably optimised away...)
in fairness, std::spaceship is about generating default relational comparison operators..
so normal you write int compare(left,right){ ..}
and then operator==(left,right){ 0== compare(left,right) }
std::spaceship will generate compare and the relational operator boilerplate..
This results in less lines of code overall in your repo. Also the remaining lines are now more focused on the desired behaviour rather the mechanics of "default comparison".
Its a goodness, "a zero cost abstraction".
Offa yer arse! I learned Rust last year. After the tutorials, I wrote a multi-threaded chess queens solution. (You should know the problem, how many queens can you put on a board without them being able to attack each other. I first did it in high school with CP/M and BASIC.)
Yes, there's a lot that will throw you. But there's also a lot that works really well, once you get a handle on it.
>After the tutorials, I wrote a multi-threaded chess queens solution. (You should know the problem, how many queens can you put on a board without them being able to attack each other
I've now got "One Night In Bangkok" stuck in my head
("I'd let you watch, I would invite you / But the queens we use would not excite you")
"I get my kicks above the waistline!"
I also just finished a variation on the Dining Philosophers problem. Sorry if that cues other sound bites. Anyways, that one was particularly devious because I had to switch to an alternate mutex library (parking lot) to solve it. Found what looks like a bug in the Rust library.
The thing I found about Rust is I unlearned some of my C and C++ practices and wrote safer code in those languages. It also made me more cognizant of code patterns which Rust would kick my arse for writing so at least I would try and isolate and make as safe as I could knowing the compiler wouldn't help me.
At the end of the day though I don't think Rust's way of thinking is actually hugely different from C and C++. It just forces you to write safe code or it won't compile at all. That incurs a lot of pain and suffering up front but saves you (and testers and customers) a lot of pain and suffering when the code actually compiles and runs.
The problem with that usually starts when I want to refactor - small steps, keep tests running, and intermediate results may not be perfect but I'll fix the edge cases later. In this very strict languages, everything in between simply refuses to compile, I lose my safety net, and often just give up and leave the bad code in place.
Rust claims "A language empowering everyone to build reliable and efficient software."
Babies will believe this and switch too it - instead of learning Rust they would code much better if they spent the time reading The Mythical Man-Month instead.
What they mean by this is a focus on zero-cost abstractions. Which is true. Rust does actually work :)
Clearly, if the first thing you heard about rust was hat it had "emphasized zero-cost abstractions" youd be scratching your head before you started to use it.
RTFM, the above statement is true once you understand what they are talking about. Its a statement about the language itself, not software engineering with the language.
It's hard to address your non sequitur so I won't directly except to say this.
Rust stops entire classes of bug from becoming compiled code. You *can't* call a null pointer because you don't use pointers in safe code. You *can't* data race because all multi-threaded structures must be protected by guards. You *can't* leak memory or double free because the compiler knows the lifetime of every object and inserts the mallocs and frees for you. Not only this make safer code but it allows *your* code to be less conservative and make better use of threads and concurrency because of the protections afforded by the language.
And unless your business likes unhappy customers, and surprise production delays then you want to catch bugs as early as possible. That's why the Rust compiler kicks your arse if you do things wrong. The sooner a bug is squashed, the quicker it is time to market and the happier the customer is.
That doesn't mean you rewrite C/C++ code for the sake of it. But if you're writing from scratch or rewriting anyway, then there is a potential huge payoff for considering switching language.
I tried IntelliJ a few years ago, after using Eclipse for a long time. Immediately after running it for the first time, I discovered that it had taken a half-gigabyte crap into a hidden directory. That's not a friendly thing to do, especially in an NFS home directory with quotas enabled. I still use Eclipse.
My medium-sized firm decided to pick a new standard language a few years back there was a heavy debate between Rust and Go. They went with Rust and haven't looked back. It's fast and it just doesn't crash. I learnt it myself this year and I think it's great. I was getting rather bored with Ruby and had considered delving deeper into C++ but it's a real monster.
The Rust library ecosystem is a little biased toward the systems programming end, but it's actually a pretty rich environment overall, and because the tools (esp cargo) are good, it's easy and low cost to add dependencies to your own projects. There have been significant efforts to cover the few remain holes that exist.
Having said that, my own experience is that it can be hard to find a the right library out of several choices, and the rust community has got a bit obsessed with semantic versioning, meaning that libraries tend to sit at version < 1 for ages; bit like googles tendency to call everything beta.
OP here. While we have many somewhat related products, it is still a work context so it tends to be backend stuff like logging, HTTP APIs, metrics, etc. but also some of the tooling we use to build and maintain the code. It works well for CLI tools.
One of the more interesting crates I stumbled across recently is fantoccini, which plugs into geckodriver (also Rust), which drives the (optionally headless) Firefox browser. That's clearly useful for testing and I've done similar things in Ruby but unfortunately I didn't get as far as actually trying it out.
Speaking of Firefox, it's well known that Rust originated from Mozilla. More and more of Firefox is being rewritten in Rust.
I haven't spent much time seeing what else is out there but I know there's an OS called Redox. I reckon Rust is capable of just about anything.
Interested, and slightly concerned, to see things like "let i = 0;".
Coders who are ancient enough will recall early Basic interpreters where every assignment statement had to start with "let". It made programs read like the more pedantic sort of mathematical proof, but it made sense in natural language.
That quickly went away, and nothing more was heard of "let" for several decades. Then Ecma-Java-Type-Script found itself in need of a keyword for block-scope declarations and decided to revive "let". The new usage makes no sense when the variable isn't initialised when it's declared, and it's inconsistent with "var" and "const", which are (contractions of) nouns. "const x = 1;" - x is a constant, "var y;" - y is a variable. "let z;" - z is a... let? letter? letcher? lett? (There are curiously few nouns that begin with "let".)
"let z;" - z is a... let? letter? letcher? lett?
It's lettuce. Green, leafy, and silly in a program.
I use C when Assembly isn't good enough...
Seriously: do-while versus while? (Versus for???). Look at the machine code. A decent compiler generates the same code for any version, and it's the most efficient implementation the architecture can provide. Those higher level constructs are so YOU, the programmer, can remember what you meant 6 months from now.
`let` has been around for ages in functional languages. Obviously having a keyword there means that you cannot do:
let my_variable = 1
me_variable = my_variable + 1
or similar spelling mistakes. Unintialized variables are fairly rare in Rust. If you want to, though, you can do it, but it still produces something that is vaguely readable because you need the type:
let s: String;
Yes, "var" and "const" are contractions of words, but Rust emphases "const" -- so "let" on it's own is equivalent to "const". To get `var` you do `let mut x = 1`; a bit wordy, but the `mut` keyword is also uses in locations other than `let` declarations, such as parameters for instance.
It all makes sense, I think and was thought out.
Upvoted. Seriously though, ++ historically came from a need to make an efficient PDP-11 instruction, and while it was cool for pointer arithmetic, I found more than a few times that it was a source of bugs in regular arithmetic. Post- and pre-increment confusion popped up more often than I liked.
Given that '+= 1' worked just fine as a post-increment ++ replacement, and pre-increment ++ could usually be easily refactored out, I started using '+= 1' everywhere.
And guess what Rust has? (Along with the other op= operators, of course.)
So it may be disrespectful, but I'll take it with pleasure.
I find Rust interesting, but I wish they had left everything that didn’t need to change the same as C(++). Like the word-order in declarations, and let vs. auto, for example. Also more C++ ABI compatibility. That would have made it possible to more easily experiment with translating some small projects.
It seems that more and more I go back to my first: FORTRAN (66 mind you). You actually needed to wrote comments to understand what YOU were doing. These new fancy ones encourage you to skip this step much to the chagrin of the "next guy" who inevitably will be your own next assignment in 6 months.
Oh, and yes it had one time do loops!
I wonder why people bother..
Keeps academics and dev system vendors in kudos, cocain and soiled doves, and gives journalists something to write/drink about, non-programmers and half-programmers something to read about, managers something to organise migration paths toward, but serves No Useful Purpose Whatsoever, and rich helpings of the opposite, in the actual development of good working code.
Not that I approve of C, though I happen to use it, but would not the world (or the computerised bits therein) be a better place if people learned to code closer to the metal, rather than getting fed distracting and abstracted BS about semantics and typing. (there are only two kinds of data, 0 and 1, get over it!)
C etc. are just Me2 languages.
FORTRAN (I'm not shouting, that's how it's spelled!), way back before I was born, defined what a compiler should be able to do. (Everything, with the program running a little slower than assembly language. A programming language for people who can't write code, same as the rest of them.) and did it well. Sources quickly grew to exceed the size of the meagre storage available at the time, so C was made. More compact than FORTRAN, more cryptic than Assembly Language, but (as FORTRAN is) notionally portable, it had only the advantage of being more compact, but the necessity to comment pretty much everything more than negated that.
I think only the cryptic mystique of the source, the lower cost and wider availability, attracted the programmer. (I'll admit myself...) though really it had no good reason to displace FORTRAN.
There have been a few "interesting" ideas. The threaded interpretive language (FORTH being the only example I can think of the name of at the moment, though I've used at least one other.), the interpreter (messy, but useful for scripting), but everything else I'm calling bullshit on!
And to "programmers". If you can't do it in assembler (or code, though painfully slowly) using a hex editor!) go flip burgers!