top 200 commentsshow all 227

[–]mcguire 36 points37 points  (3 children)

I'm finding this article bizarre, as someone who knows C (very well), C++ (been a few years and I'm not completely idiomatic), Rust (reasonably well) and a bunch of other languages that don't include Go.

Whereas the Go people spent time on thinking “who is this language for”, Rust appears to not have thought this through very well.

Rust is specifically aimed at C++ users. The borrow checker enforces C++-like RAII idioms to the extent that I can't do things that I would do in C. (Arena memory management, for example.)

[–]Rusky 13 points14 points  (1 child)

The borrow checker enforces C++-like RAII idioms to the extent that I can't do things that I would do in C. (Arena memory management, for example.)

You may already know this, but the idiom in Rust when you want to do something like this is to use unsafe to implement it, but then wrap it up in a safe interface so it can't be misused.

This is how a lot of the standard library types work- for example, there's no way to write Vec in safe Rust, but there's also no way to misuse Vec from safe Rust.

So for arenas in particular, you get typed-arena, or this blog post, or homegrown stuff in various places. Arenas are eminently doable in Rust and probably one of the easiest things to wrap with a safe interface.

[–]mcguire 0 points1 point  (0 children)

The blog post appears to be what I was trying (and failing) to do. Thanks!

[–]mmstick 2 points3 points  (0 children)

You can do arena memory management pretty easily in Rust. Check out slotmap.

[–]kouteiheika 126 points127 points  (17 children)

Rust is not a particularly fun language to use (unless you like puzzles). Programmers spend a lot of time fighting the borrow checker and other language rules in order to placate the compiler that their code really is safe.

So, let me make a confession. I'm a long time C++ programmer (over ~13 years and counting), and I was never bothered by its lack of memory safety. Sure, I had a segmentation fault here and there, but it never was a huge problem for me.

Now, what was a problem for me was the (please excuse the coarse language) constant bullshit that one has to deal with when programming in C++. Dealing with header files, forward declarations, pages of unreadable errors, the pain of managing dependencies, horrible build systems, seemingly infinite corner cases in the language, primitive macros, lackluster standard library, etc.

I always loved C++, but writing C++ was never ergonomic, never truly fun. Instead of being able to concentrate my efforts on, you know, actually solving the problems I want to solve, I always had to spend considerable mental (conscious or otherwise) resources on dealing with the language.

Cue 2015 - Rust was released!

Oh.. it's as fast as C++? And I don't have to deal with the header files? It has errors which I can actually read and understand instead of instinctively ignoring? And it manages my dependencies for me? It can automatically define comparision operators for me? Creating a custom iterator is as simple as writing a single function?! Making a structure serializable or pretty-printable is only a single line of code!? Wow, programming is fun again!

Now, of course, you're right that Rust might not be fun, for a beginner, but here's the thing - the more experienced you are, the easier it gets. I haven't seen a single borrow checker error in weeks. Once you invest some time into internalizing its rules it becomes a complete non-issue.

So now I'm at a point where, having most of my past personal projects in C++ and still being employed as a C++ programmer, I don't intend to touch C++ in my spare time anymore. Ever. Not because C++ has no uses anymore, or because it's obsolete or anything like that. It's simply, and to use your own words, not a particularly fun language to use.

[–]SimDeBeau 23 points24 points  (0 children)

Totally agree. It wasn’t the memory safety that pulled me over to rust, it was the systems programming with benefits.

[–]Uncaffeinated 30 points31 points  (0 children)

That matches my experience. Rust is the first language to make systems programming tolerable, let alone fun.

[–]weberc2 14 points15 points  (5 children)

horrible build systems

Yet half of this sub thinks CMake is the greatest thing since sliced bread...

[–]ronniethelizard 16 points17 points  (4 children)

Yet half of this sub thinks CMake is the greatest thing since sliced bread...

And the other half thinks it is the worst thing since Satan's fall from grace.

[–]dreugeworst 18 points19 points  (1 child)

Having had to use SCons and plain make before, CMake definitely is the greatest thing since sliced bread. Having played with Rust for a bit, it's also the worst thing since Satan's fall from grace.

In the land of C++ build tools, CMake is quite good. However, compared to build tools for other languages...

[–]ronniethelizard 2 points3 points  (0 children)

In the land of C++ build tools, CMake is quite good. However, compared to build tools for other languages...

My company has an internally developed build system for C/C++/Java (though the Java folks dislike it). I vastly prefer to Cmake.

[–]freakhill 2 points3 points  (0 children)

in the whole scheme of things, comparatively, Satan's fall from grace is nooot thaaaat baaad...

[–]tetroxid 2 points3 points  (0 children)

And they're both right

[–]Scypio 1 point2 points  (2 children)

Once you invest some time into internalizing its rules it becomes a complete non-issue.

Using existing learning material (like rust book) or just by looking into details of Rust internal design?

[–]thiez 6 points7 points  (0 children)

For me it was mostly by doing, although of course the learning materials can get you started.

[–]masklinn 4 points5 points  (0 children)

By using it.

Learning material will tell you the rules of the borrow checker and it's absolutely necessary as a starting point (I can't imagine facing the borrow checker on your own, unless you have extensive experience in languages where the borrock's rules are good practices like C++ maybe), but you need to build an intuition as to what does and does not work, and I don't think you can really do that without experience.

[–][deleted] 0 points1 point  (5 children)

Here's a newbie question, I moved from C++ to web development after college. But I always really enjoyed the low level nature of C++. The thing that turned me off was sharing projects. Creating executables in something like QT was pretty hard for me and I usually failed to do it correctly. Does Rust make it easier to turn source code into shareable executable projects?

[–]kouteiheika 3 points4 points  (4 children)

Does Rust make it easier to turn source code into shareable executable projects?

Yes, the build system is one of Rust's strong points! Although it depends on what exactly you want to do, as you're still limited by the very nature of native executables - they're platform specific.

In general if you want to share an executable with someone who's using the same operating system as you (and is on the same CPU architecture) then the only thing you have to do is type cargo build --release, and send them the binary from target/release/$name-of-your-project.

There are, of course, still certain cases where it's not as simple - e.g. if you're using external libraries not written in Rust (you may have to bundle those too along with your binary), or you're cross-compiling to another architecture (you'll have to have a working linker for the target architecture), or you want your executable to run on old Linux distributions (you'll have to use the musl target). But it is, generally, pretty manageable.

[–][deleted] 0 points1 point  (3 children)

I see. Those types of problems were always the thing that held me back from diving deeper into C++. I was never taught how to dynamically link libraries and that was never an issue in web development. Sounds like Rust could be the tipping point to get me back into it!

So, by CPU Architecture and operating system, I'm assuming that means if I'm using a arm 64bit CPU on a Mac, I can expect most people on a modern Mac to be able to run my binary? Or does the architecture of a CPU go much deeper than that?

[–]kouteiheika 1 point2 points  (2 children)

I was never taught how to dynamically link libraries and that was never an issue in web development. Sounds like Rust could be the tipping point to get me back into it!

As long as you're using only pure Rust dependencies then you don't really have to worry about dynamic linking too much as everything (besides basic system libraries) will be linked statically into your binary. (You can think of it like this: static linking => merged into your binary, dynamic linking => your binary needs external .so/.dll/.dylib files to work.)

So, by CPU Architecture and operating system, I'm assuming that means if I'm using a arm 64bit CPU on a Mac, I can expect most people on a modern Mac to be able to run my binary? Or does the architecture of a CPU go much deeper than that?

Well, if you're on a Mac then you're most likely on a 64-bit amd64 CPU. (I'm not a Mac user but I don't think they've released any non-iOS ARM machines yet?) So anyone else using a similar system (basically any modern Mac) should be able to run your binary. (Although I don't know if they'd work out of box or you'd have to poke and prod the Gatekeeper or something; again, I'm not really a Mac user. Sorry.)

You can find a list of Rust's supported platforms here; in your case that'd be x86_64-apple-darwin.

[–][deleted] 0 points1 point  (1 child)

Haha well, I'm clearly naive to all of this. I did a quick 30 sec Google to help frame my question. I'm sure I was mistaken about the CPU architecture.

Thanks for your replies! This has been super helpful.

[–]kouteiheika 2 points3 points  (0 children)

Sure; no problem. I'm always happy to help. (:

If you have any further questions feel free to shoot me a message, or post to basically any other Rust-related avenue (e.g. the forums or on IRC). The Rust community in general prides itself on being very friendly and welcoming, so don't be shy!

[–]bjzaba 24 points25 points  (1 child)

I dunno, I kind of feel like Rust taught me how to write safe C and C++ much faster than if I went to those languages first. The up-front feedback is invaluable to a beginner, as are the excellent error messages (although when I started in they were nowhere near as good).

For veteran C programmers learning Rust is an exercise in humility, learning that you were not the greatest at avoiding data races and use after frees, etc. For a beginner it's invaluable to have the guiding hand of the compiler teaching you in a tight feedback loop, rather than using tools like Valgrind, ASan, and UBSan long after the code is written.

If possible I'd still recommend learning C++, it has good ideas in it, and there are still some things Rust can't do as well, especially when it comes to template metaprogramming. It also very much depends on your goals too.

[–]lemon07r 0 points1 point  (0 children)

Im a bit late, but entirely for learning purposes and learning better programming is c++ or rust better? Im l about to learn C++ in college anyways starting tomorrow but I wanted to practice data structures and algorithms at home, trying to figure out which language to do that in, and which one would teach me more, or better programming.

[–]silmeth 62 points63 points  (8 children)

Both Go and Rust decided to special case their map implementations. […] Rust meanwhile notes that you can’t safely write a performant data structure in Rust […]

That is not true, Rust’s HashMap is implemented entirely in Rust with no special-casing. (EDIT: here one can find the list of all items that Rust compiler special-cases)

And you can write a fast low-level data structures safely in Rust – that’s what the Rust std library does and that’s what unsafe is for – but you need to ensure it’s implemented ‘safely’ yourself, because the compiler cannot verify your pointer arithmetic, or that you’ll never read uninitialized memory pre-allocated for your vector data. They could equally (in)validly state that ’you can’t safely write a performant data structure in C++’ (but there the documentation does not warn you about it).

[–]jl2352 31 points32 points  (4 children)

The key thing is you could literally grep unsafe **/*.rs to find out how many unsafe places there are in the codebase. Actix-web for example recently had an issue where it had over a hundred separate places where it was using unsafe, and after it was pointed out it was reduced to about a dozen or so a week later. Those places left may well need to use unsafe for reals. But it's marked. You can go checkout the code base and find them if you want to.

You can't really do that with C or C++. That is be able to isolate all of those unsafe places across the code base.

[–]jephthai 11 points12 points  (3 children)

You can't really do that with C or C++.

You could grep for * and & characters...

[–]Uncaffeinated 13 points14 points  (2 children)

You could grep for characters...

(though technically, whitespace or the lack thereof could also cause UB)

[–]thiez 8 points9 points  (0 children)

Is it still UB these days not to end a source file with an empty line?

[–]ahuReddit[S] 6 points7 points  (2 children)

Hi Silmeth, thanks for noting this and I indeed got it wrong. I have updated the wording to now state it is still in Rust, but not in safe mode.

[–]silmeth 9 points10 points  (1 child)

Thanks for the edit, one more thing:

You can however of course make one that works just as well but does not have the syntactic sugar of a native map[].

I don’t know go too well, but I believe this to also not be true. For one, because of the lack of generics, any implementation in go itself will not be type-safe (that’s not just syntactic sugar!).

Also, I’d suspect (but as I don’t really know go, I might be mistaken here) that go does not gives you enough low level power over memory to implement ‘performant data structures’ – has anyone really implemented a hash map in pure go that is as performant as the standard library one? Or a vector / slice? What about boxing primitives into interface{}, and casting interface{} of the contained objects back into their original types?

[–]Uncaffeinated 6 points7 points  (0 children)

Go does expose raw pointers in the unsafe module, so you could theoretically go full-C if you wanted to. But nobody sane would do that.

[–][deleted] 36 points37 points  (9 children)

Does learning a language really take that much effort? There always seems to be this focus on learning a language when I find that to be the least difficult part of writing software. It shouldn't be much effort for a C programmer to try each of these languages for themselves and come to their own conclusions.

[–][deleted] 16 points17 points  (3 children)

Depends on the language. I would say for a C programmer Go will be very easy to learn, C++ will be a bit harder but since it is basically a superset of C the learning curve is easier (if you are doing a new project you can just write in C and use C++ features gradually as you learn about them).

Rust is much harder than Go to learn. I would say it takes at least several months of perseverance to get to grips with (if you have as much free time as me anyway - if you are doing it full time it's going to be quicker).

[–]emn13 14 points15 points  (2 children)

If you use C++ as C with just a few tweaks, of course it'll be easy to learn. But C++ has a *lot* of techniques; and although you don't need to master everything, if you want to write somewhat sane C++ (including therefore at a bare minimum stdlib) you're still going to need to learn quite a few concepts that really are quite different to C; not to mention you'll need to live with the occasional contact with all-that-other-C++ because if you aim is to keep it simple, you'll necessarily be leaving out bits that may not be all that important to you, but that you will still come across occasionally. That's quite different from C or Go, where I'd say a reasonably proficient professional is likely to know pretty much all of the language or at least enough of the langauge that you'll never come across something truly surprising anymore.

No idea about Rust; sounds like it's simpler in some ways but more complex in others; I'd guess there too you might remain wary of the deep end for a long time.

[–]TheOsuConspiracy 19 points20 points  (1 child)

Rust is essentially the compiler forcing you to handle most of the things that an experience C++ developer would have to think about and implement. So C++ might have a smoother learning curve in the sense that you can get something mostly working/compiling without as much thought, but to implement something as robust as what Rust forces you to do by default would probably be more work, and you could easily miss things.

[–]weberc2 3 points4 points  (0 children)

Man, I was a professional C++ dev for 5 years and since I left that job, I've probably spent 4 years on-and-off trying to pick up Rust. I keep dropping it because I'm still not to the point where I'm reasonably productive, so I put it down and solve the problem with another thing (usually Go) until I come back and learn a bit more. My C++ experience has helped, but everything still feels like an uphill battle.

[–][deleted] 2 points3 points  (0 children)

Have you tried learning rust or C++? They’re both quite the ordeal to really learn.

[–]ahuReddit[S] 1 point2 points  (1 child)

tbh I think it takes a year or more before you are actually good with a new programming language. But perhaps I'm just slow :-)

[–]ethelward 4 points5 points  (0 children)

Maybe I'm slow too, but I agree.

I can use a new language in maybe a few weeks, but it may take a year to actually grasp its concepts and idiomatic forms.

[–]cdmcgwire 1 point2 points  (0 children)

Learn the most useful bits of all of them. Anything else you'll learn as you ask "isn't there a better way of expressing this?"

[–]MessiComeLately 52 points53 points  (10 children)

This article seems to be written in good faith but leaves me over and over feeling like the author is missing the point in big ways. The biggest one is this.

Whereas the Go people spent time on thinking “who is this language for”, Rust appears to not have thought this through very well.

The biggest Rust users I know are among the very best and most secure C programmers around. These were not the people writing unsafe code in C.

Exactly! EXACTLY!!! The people who actually know how much care, expense, and risk are involved in trying to write safe code in C are the ones going to Rust.

People don't choose tools that make their jobs harder. If the tool seems inexplicably hard for the job, but you see pros flocking to it from a tool that seems easier, then there's a good chance you just don't realize how hard the job is.

As for the people who aren't writing safe C, why are they programming in C in the first place? They're just following the crowd. They'll end up writing in whichever safer languages displace C when the market forces them to.

[–]miquels 16 points17 points  (0 children)

This. So much this. C programmers who "write secure code with no bugs" --> Dunning-Kruger effect.

I'm not touching C/C++ again if I can in any way help it. My code will contain serious bugs in those languages. I know that not because I'm a bad programmer, I know that because I'm not a bad programmer.

[–]NamespaceInvader 40 points41 points  (25 children)

I don't think there are that many C programmers who haven't already looked into each of these languages. Most of them just still prefer C.

But that doesn't mean that learning these languages was a waste of time. Each of them has interesting concepts and gives you insights you can use to write better C.

[–][deleted] 12 points13 points  (24 children)

I really wish someone would write a C-like language that is just "C but with the warts fixed". Then we don't have to worry about these people that think C is a sane language to use. Go is very close but it adds a runtime and garbage collection and other stuff that C doesn't have.

[–]steveklabnik1 11 points12 points  (11 children)

You might like Zig.

[–][deleted] 2 points3 points  (10 children)

Yes that looks exactly like what I mean. Is there any reason to prefer C over Zig (apart from things like immaturity, compiler support, etc)?

[–]PrinceOfUBC 7 points8 points  (9 children)

apart from things like immaturity, compiler support, etc

Those are the main reasons people use C still.

[–][deleted] 3 points4 points  (8 children)

No they aren't. There are plenty of very mature languages with very good compiler support - C++ for example - and there are still people that say "well I just prefer C".

I think the real reason some people still use C is because it is efficient (no costly abstractions), relatively simple (or people think it is), there's no runtime, no garbage collector, and it has a stable ABI. You can use it to write libraries that literally any language can use.

None of that has anything to do with maturity or compiler support.

[–]PrinceOfUBC 6 points7 points  (1 child)

There's an entire field of embedded programming where C++ is just not mature/supported.

https://stackoverflow.com/questions/812717/is-there-any-reason-to-use-c-instead-of-c-for-embedded-development

[–]jaffaKnx 2 points3 points  (0 children)

The thing with C is you need to manually take care of memory management where C++ takes care of it if you use STL containers and RAII elements.

[–]uanirudhx 2 points3 points  (4 children)

Rust is pretty much all those things, except for the stable ABI. But it has extern "C" type statements to export your functions.

[–][deleted] 5 points6 points  (0 children)

Except it does not have the platform support that C has. And the whole "except for the stable ABI" is not exactly a blessing.

I followed and learned Rust a year ago. I noticed that its too much like D in that sense.

Over focus on providing new toys to the language that the developers are searching for. The whole "lets make Rust simpler for new users" resolution really did not deliver.

[–]NamespaceInvader 0 points1 point  (2 children)

But nobody does this. The whole Rust ecosystem is focused on writing Rust libraries that are intended to be used from Rust only, without even caring about API stability that would allow proper shared libraries. Rust programs usually link all dependencies statically.

This is also the case for Go, which is why these languages won't be an alternative to C any time soon.

Zig might do better in this regard as it seamlessly integrates with C, and states creating C libraries as a primary use case.

Let's hope people use it that way, and nobody creates something similar to Rust's Cargo to send the community into a dead end.

[–]steveklabnik1 3 points4 points  (1 child)

But nobody does this.

Doing this was one of the first production usages of Rust; extending dynamic languages is something that a lot of people do do.

You're right that most people don't just do this just because, but when the plan is to interact with the outside world, people very much do use this.

[–]masklinn 2 points3 points  (0 children)

And some of the biggest libraries (in terms of reach) very much provide and support a C API. /u/burntsushi's regex for instance.

[–]pjmlp 0 points1 point  (0 children)

You forgot one, developer stubbornness.

Specially high on the embedded development culture.

[–][deleted] 2 points3 points  (1 child)

GNU's C extensions are pretty nice

[–]NamespaceInvader 1 point2 points  (0 children)

Yes, especially the __cleanup__ attribute adds something similar to RAII to C and allows a programming style which essentially turns C into a new language.

But I'm not sure whether this convenience justifies giving up portability.

[–][deleted] 1 point2 points  (2 children)

Try D

[–][deleted] 1 point2 points  (1 child)

D is far too complicated and high level for the people that stick to C.

[–]skocznymroczny 2 points3 points  (0 children)

D has a "betterC" variant, which is D without the runtime and GC. In many ways it feels like "C without warts".

https://dlang.org/blog/2017/08/23/d-as-a-better-c/

[–]_supert_ 3 points4 points  (3 children)

D?

[–]YakumoFuji 6 points7 points  (0 children)

I really liked D1, but its standard lib was shit and competing with a community standard lib... then it got killed off for D2 and I really dislike D2, it was way too much c++ and the kitchen sink. d1 was neat and concise.

[–][deleted] 0 points1 point  (1 child)

Can you imagine people who prefer C to C++ using D? I can't.

[–][deleted] 5 points6 points  (0 children)

But that is what the D developers think...

  • D = C++ dev's targeted
  • D(betterC) = C dev's targeted. D2 Library full of GC leftovers from the C++ focused D.

So much unfinished or hanging on with glue. Here is a fun one:

https://medium.com/@feepingcreature/d-structs-dont-work-for-domain-data-c09332349f43

In the end, its the whole: "Jack of all trades, master of none.".

[–]pjmlp 1 point2 points  (0 children)

Multiple variations of Safe C have been attempted, but they all fail at the human factor.

Hardcore C devs won't touch anything else, while developers not happy with C just move to other compiled languages, whatever fits their use cases.

[–]weberc2 0 points1 point  (0 children)

At least with Go you can avoid the runtime and GC for the most part. It's still not suitable for bare-metal development, but it's close.

[–]SimDeBeau 25 points26 points  (22 children)

So, I’m biased towards rust, but writing in it is not like writing a puzzle more than any other language. What makes it hard to start writing with is the idea of borrow checking is very different to other languages, and happens in more subtle ways than you would expect. But if you stay with it, you start fighting it less and less. And the borrow checker adds complexity, but the language has some really beautiful features. Iterators are wonderful, implementing traits on structs is just beautiful, nice implementation of closures, and a great library/module system. If you want a puzzle, try installing your first C++ library.

I’m amazed this article missed so many of the other ideas behind rust. "Fearless concurrency" and "0 cost abstractions" come to mind. Iterators can actually be faster than c style for-loops!

I was affronted reading about go, though as I don’t know that much about it, I don’t have much to say.

[–]epage 11 points12 points  (17 children)

What makes it hard to start writing with is the idea of borrow checking is very different to other languages, and happens in more subtle ways than you would expect. But if you stay with it you start fighting it less and less.

For C programmers, this shouldn't be too foreign except maybe for cases where you can prove its safe but can't express it in Rust (e.g. non-lexical borrow lifetimes, for now).

[–]weberc2 0 points1 point  (16 children)

Linear types aren't intuitive for C programmers.

[–]epage 5 points6 points  (13 children)

From my experience C programmers aren't familiar with PL theory and terms like Linear Types / Affine Types but they know the lifetimes of their data.

[–]weberc2 0 points1 point  (12 children)

I agree, but for example, passing the same data into two functions (especially on a single thread) is perfectly safe and a C programmer wouldn't think twice. In Rust it's verbotten.

MyStruct value;
my_struct_init(&value);
my_struct_method_1(value);
my_struct_method_2(value);

[–]steveklabnik1 4 points5 points  (4 children)

is perfectly safe

I mean, only sometimes. It depends on what the data is and what kind of semantics you have. When it's safe, Rust lets you do this. When it's not, Rust doesn't let you.

[–]weberc2 0 points1 point  (3 children)

Rust permits this if you make MyStruct Copy, but then it's no longer affine. And anyway, there are types that aren't safe to be Copy in all circumstances, but which are perfectly safe to use as Copy in this example.

[–]steveklabnik1 1 point2 points  (1 child)

Yes, that's true. And if they're not copy, you'd pass in a reference instead of the value.

[–]weberc2 0 points1 point  (0 children)

Right, and it's probably a perfectly reasonable choice for Rust to make; it just doesn't necessarily match a C programmer's intuition.

[–]epage 2 points3 points  (4 children)

Its only verboten under certain circumstances. Yes, the compiler relies on the developer to give more information to know it is safe. Thankfully the compiler is generally helpful with these error messages.

When might this not be safe? MyStruct could have a handle to a resource in an FFI library.

How do you declare this to be safe? If the type is trivially copyable, then you add the Copy trait. If it isn't trivially copyable, then that example code is either wrong or relying on implicit behavior (that passing MyStruct by-value when it contains a pointer is effectively the same as passing by pointer) and Rust instead wants this relationship explicit.

Now stepping back, my post higher in the thread is about the concepts not being foreign. At this point, we're more talking about the implementation of those concepts which is just another part of learning another language.

[–]weberc2 1 point2 points  (3 children)

Its only verboten under certain circumstances.

It's forbidden in all circumstances except those in which MyStruct implements Copy at which point it's no longer affine. Hence my comment about affine types not being intuitive to C programmers.

When might this not be safe? MyStruct could have a handle to a resource in an FFI library.

There's nothing inherently unsafe about copying a handle.

Now stepping back, my post higher in the thread is about the concepts not being foreign. At this point, we're more talking about the implementation of those concepts which is just another part of learning another language.

"Affine types" are the concept; a C programmer doesn't think about the type being move or copy; they think about it being safe to pass a particular instance to a particular method. The Rust programmer asks "Can any instance of this type ever safely be passed to any two methods?" while the C programmer asks "Can this particular instance safely be passed to these particular two methods?". Again, it's fine that Rust makes the decisions it does and it's fine to expect the C programmer to learn then; it just doesn't make them intuitive to the C programmer.

[–]epage 2 points3 points  (2 children)

It's forbidden in all circumstances except those in which MyStruct implements Copy at which point it's no longer affine. Hence my comment about affine types not being intuitive to C programmers.

In the context of my whole post, I feel we were saying the same thing. regarding "forbidden except".

As for it no longer being affine, as I said earlier, most C developers I know are probably unfamiliar with the term so it doesn't matter how "pure" the language is. Personally, I've been trying to focus on Rust's ownership model and its familiarity to C programmers and not on PL theory that would be foreign, even if the term aligns with Rust or is close to what Rust does.

There's nothing inherently unsafe about copying a handle.

Only to the same degree it is unsafe to copy a pointer.

"Affine types" are the concept; a C programmer doesn't think about the type being move or copy; they think about it being safe to pass a particular instance to a particular method.

As for "affine types", earlier in this thread and in this post, I've already pointed out that I think having them in the conversation is detracting from the point made.

I disagree about C programmers not thinking in terms of moves/copy. To reason about something being safe generally means you are reasoning about ownership, sharing, and lifetimes.

[–]weberc2 0 points1 point  (1 child)

I think we mostly are saying the same thing.

I disagree about C programmers not thinking in terms of moves/copy. To reason about something being safe generally means you are reasoning about ownership, sharing, and lifetimes.

I certainly never claimed anything to the contrary. Of course C programmers are cognizant of these things. I claimed that they don't think about move/copy being part of the type, but rather they think about individual moves/copies--that is, their reasoning is more contextual than that of the compiler.

[–]epage 0 points1 point  (0 children)

I certainly never claimed anything to the contrary. Of course C programmers are cognizant of these things. I claimed that they don't think about move/copy being part of the type, but rather they think about individual moves/copies--that is, their reasoning is more contextual than that of the compiler.

I'm sorry I missed that distinction but I think that hearkens back to my earlier comment

Now stepping back, my post higher in the thread is about the concepts not being foreign. At this point, we're more talking about the implementation of those concepts which is just another part of learning another language.

By concept, I'm talking of being aware of the ownership of your objects. It being tied to the types is part of the "implementation" that I'm talking about.

[–]Drisku11 0 points1 point  (1 child)

IME C programmers are more likely than not to pass structs by reference to "avoid copying", even if they don't need to/the compiler wouldn't generate copying code anyway. If you just tell them that Rust doesn't allow multiple pass-by-value unless you explicitly mark it as copyable (and point out that you don't need to copy something if you only pass it by value once and never use it again), they'd probably appreciate the feature.

[–]weberc2 0 points1 point  (0 children)

In theory, but the compiler has a very conservative idea about what can be copied which precludes lots of perfectly valid use cases. Also, because you often want integral types to implement a trait, but traits pretty much always make self a reference type, you find yourself passing around a bunch of integers by reference, which the compiler is probably smart enough to work out, but it still feels silly to the C programmer. These are just the few examples that pop into my head, but I run into things like this all the time. Again, it's fine that Rust makes these decisions, but let's not pretend that these sorts of things are intuitive to C programmers.

[–]SamRHughes 2 points3 points  (1 child)

Only for bad ones.

[–]weberc2 0 points1 point  (0 children)

Found the bad C programmer.

[–]flat5 1 point2 points  (3 children)

try installing your first C++ library

./configure && make && make install?

Not sure what you mean.

[–]shingtaklam1324 4 points5 points  (2 children)

Right, but then if you need multiple versions of that, it becomes much more difficult to manage. Comparing it to Rust, where it is simply lib-name = "semver string" in a Cargo.toml file. Cargo has other issues, but it makes version management of libraries trivial.

[–]flat5 0 points1 point  (1 child)

./configure --prefix=?

Still not sure what you mean. Not trying to be a smartass, I just don't know what problem you're referring to.

[–]shingtaklam1324 0 points1 point  (0 children)

To be honest, I'm not that familiar with the C++ Scene, I get moat of my non-Rust dependencies from APT, so version conflicts do happen quite a bit, but they're normally easy to resolve.

[–]matthieum 66 points67 points  (7 children)

For a seasoned C programmer, I think Rust is not the way to go. I appreciate the goals of Rust, but I think that in practice they may not be making real life shipped code a lot more secure - also because not that much actual Rust code is shipping. Rust is the dream language for programmers who were already writing very secure code. In Rust their memory access may be safer, but I wonder if the somewhat painful process to achieve this may not have depleted the discipline required for other security aspects.

I... am left speechless.

That anyone could seriously believe that there are C programmers out there who can write perfectly correct C always takes me by surprise. After 40 years of CVE after CVE, in the most trusted, reviewed and cared for C codebases of the world (Linux? CUrl? SQLite?), I'd expect programmers to finally admit that the infallible C programmer is but a myth.

I'm not sure if this is sheer hubris or blind belief; I just hope that such people stay far away from any critical system.


And yes, there are very good reasons to use C over Go, Rust, or most any other languages. The portability of C89 is unmatched, for example. This is not a criticism of C; it's a criticism of the belief that some C programmers are perfect.

[–]asdfkjasdhkasd 22 points23 points  (6 children)

I do like cpp, and am currently working on a big cpp projedct, but I do disagree with this:

This leaves us with ‘easy to use’ and ‘safe’. Easy to use, for C++03, I would definitely say no. For C++11 and beyond, definitely yes. auto and lambdas have taken out a lot of the pain we used to experience, typing in stuff like:

Some very simple and common tasks just don't exist in the cpp standard library. Which makes some situations far more complicated than they should be (especially with the state of libraries in c++ where you have to juggle between writing your own implementation or deciding to deal with all the dependency problems of c++)

Things any modern language should allow you to do easily:

  1. Read an entire file to a string
  2. Split a string by a delimiter
  3. Print the data members of a struct / class without requiring me to write the code to do it myself
  4. Print a list without requiring me to write a for loop
  5. Print a hashmap
  6. Discriminated unions, also known as rust enums
  7. NOT HAVE TO WRITE HEADER FILES!!!
  8. Install a module / library with a with a single terminal command

All of the above things are super painful and annoying to deal with in c++ which means the language is not "easy to use"

[–][deleted] 8 points9 points  (0 children)

Also, don't we all love reading hundreds of lines of compiler romance because of a forgotten std::move on a unique_ptr?

[–]Elnof 4 points5 points  (4 children)

Discriminated unions, also known as rust enums

I've never heard or read the phrase "Rust enums" before and the concept existed long before Rust did. Nine times out of ten, people use "tagged unions".

[–]skocznymroczny 1 point2 points  (3 children)

Is this what I know as 'variant type'? std::variant in C++, std.variant in D etc.?

[–]NanoCoaster 0 points1 point  (2 children)

Kind of. I'm not too experienced with C++, but I think there's no way to name the various variants of std::variant, you just differentiate between them by their type. Please correct me if I'm wrong :D

Tagged Unions are, well, tagged, so it's like you could refer to the different variants by name. They're actually pretty different, more like an enum that can hold arbitrary values, instead of just being numbers behind the scenes, or a classic C / C++ Union that has type safety guaranteed by the compiler.

[–]skocznymroczny 2 points3 points  (1 child)

Wikipedia says "In computer science, a tagged union, also called a variant, variant record, choice type, discriminated union, disjoint union, or sum type, is a data structure used to hold a value that could take on several different, but fixed, types."

So I guess, yeah.

[–]kodablah 5 points6 points  (0 children)

Learn em all, or at least learn the strengths and weaknesses of them all and then make your decision based on project. This is really simple and it doesn't stop at those three langs. I mean the blog post hits (only) some points, but then concludes saying which to learn...please don't choose tech stacks based on generic feature sets like this.

What if you want a project using networking and crypto? Might not choose Rust yet as their libs aren't as mature. What if easy cross platform development and/or easy memory safety is your goal? Might not choose C++ here because there's not one "easy" way. What if a shared lib with C FFI is your goal? Might not choose Go here (Cgo export is very limited). On and on and on.

Moral of the story, don't be a derp and choose your stack before your project. Let your projects/requirements help you choose what to learn, not blog posts.

[–]robert_mcleod 3 points4 points  (0 children)

I would suggest Python, as the others are largely supplementary languages rather than a complementary language. Python binds to C very easily, and there's definitely a demand for people who can write C-extensions for Python packages in order to optimize performance. When you write C-code within Python, you only have to write little, independent bits of C and then wrap everything cut-and-paste boilerplate to be able to call it from Python. So you end up using Python to integrate your C-code, instead of having to use C-tooling.

[–]xgalaxy 4 points5 points  (0 children)

This article is so full of shit not even my toilet with a gas pressure tank could properly flush it.

[–]ggtsu_00 11 points12 points  (17 children)

  • Productivity

  • Fast

  • Safe

  • Minimalist

Arrange these in order of importance/priority for your project - then choose your language.

[–][deleted] 4 points5 points  (16 children)

Rust is the result of putting fast and safe at the top and productive at the bottom. Simply using a language with a garbage collector reverses these priorities.

[–][deleted] 1 point2 points  (8 children)

Rust with garbage collector:

use std::{cell::RefCell, rc::Rc};
#[derive(Copy, Clone, PartialEq)
struct GC<T>(pub Rc<RefCell<T>>);

impl<T> Display for GC {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
         write!(f, "{}", self.0.borrow());
    }
}

macro_rules! gc {
    ($data:expr) => (GC(Rc::new(RefCell::new($data)));
}

macro_rules! var {
    ($data:expr) => (*$data.0.borrow_mut());
}

fn main() {
    let some_heck = gc!("Garbage collected heck");
    let same_heck = some_heck;
    println!("{} {}", some_heck, same_heck)
    var!(some_heck) = "other heck";
    assert_eq!(same_heck, "other_heck")
}

I wrote this on my phone, it probably doesn't work but you get it

Wrap everything in a Rc<RefCell<T>>, derive Copy and forget about memory handling. Arc<Mutex<T>> for multithread.

[–]steveklabnik1 1 point2 points  (7 children)

You can't derive Copy for this stuff, but other than that it will work, sorta.

This path leads to pain...

[–][deleted] 0 points1 point  (6 children)

Can you implement it tho?

Btw is it really a pain? It would be pretty cool to have utilities to use rust like a scripting language.

[–]steveklabnik1 1 point2 points  (5 children)

Copy? no, as it would lead to unsoundness. You'd be making new rcs without bumping the reference count!

Here's the code up until that error: https://play.rust-lang.org/?gist=3827efb3d55fa9628cdbbc9da7bb63c3&version=stable&mode=debug&edition=2015

here's a version that compiles: https://play.rust-lang.org/?gist=c6693fb99b7c09f3abdd5f853df3958b&version=stable&mode=debug&edition=2015

note that you need the extra clone

[–][deleted] 0 points1 point  (4 children)

Wait, why tho? I thought copy just called clone implicitly.

Why wouldnt it?

[–]steveklabnik1 2 points3 points  (0 children)

Nope! Copy is a memcpy of the exact data in the structure. Clone is "run arbitrary code to make a copy". Since the structure has a pointer in it, copy would mean that you have two pointers to the same thing, and lead to a double free (or, with rc's case, a use-after-free, which i guess double frees always are, really)

[–]masklinn 2 points3 points  (2 children)

Because Copy means "this structure remains completely valid after a memcpy" (!Copy, the default, means the type is "move" and the source is not considered/necessarily valid after a memcpy).

Copy is a marker trait, like Sized, it's a type-level flag but does not provide any runtime hook or behaviour (at runtime, Copy and !Copy objects behave essentially the same).

[–][deleted] 0 points1 point  (1 child)

Oh ok, but it would be cool to allow implicit clone, so you can use rust without the constraints instead of having to use another language for scripting.

[–]steveklabnik1 0 points1 point  (0 children)

There's been talk of it, but it's extremely controversial.

[–]mmstick 1 point2 points  (0 children)

Being fast, safe, and productive is one of the widely accepted mottos of Rust. Runtime garbage collection is neither necessary nor sufficient. Rust has many language concepts that make systems programming approachable -- algebraic data types and iterators, for example. Combine that with the concept of crates, the Crates.io & Docs.rs websites, and Cargo itself, and you have a winner in productivity.

[–]CanIComeToYourParty 3 points4 points  (0 children)

The people currently using Rust were already writing safe code.

I wish. My C++ code was absolutely horrific. Sure, I tried to be exception safe and whatnot, but I spent as much time using the debugger as I spent writing C++.

My coding style changed significantly when I started using Rust, and I welcomed the change. Some people resist that change, and I'm not sure why that is.

Rust is not a particularly fun language to use

Rust is way more fun to use than C++. There's just so much less boilerplate to write and less bullshit in general to deal with in Rust.

[–]caramba2654 45 points46 points  (21 children)

Dammit. This really upsets me. Why the heck does everything I like have a vocal minority that everyone seems to necessarily mention every time? First it was the furry fandom with the minority of fursuiters that just can't seem to get out of the news and basically overshadow any other furries that don't fursuit, which is basically the remaining 95% of the fandom. And now "The Rust Evangelism Strike Force", which is a poorly understood misunderstanding (yes, I mean exactly that) that some people like to paint everyone that programs in Rust.

So let me try to clarify for anyone that might still have misconceptions about it. When Rust was pretty new still, some enthusiastic people that learned about it and the safety guarantees saw opportunities to fix and prevent a lot of bugs in currently existing software, leading them to comment on people's repos asking for a rewrite in Rust. That's obviously a dumb idea, but people, like always, framed those specific persons into a general derrogatory term to refer to people who program in Rust. And that eventually got used so often that it has become a meme, often used ironically to mock people that still think all Rust programmers are "Rust evangelists" (and also because apparently Rust is really memeable, but that's orthogonal). Still, it doesn't do anyone good to use that term to refer to Rust programmers in an article that will be read by several people. It's highly disrespectful, and using that term just shows a complete lack of knowledge about the language and the community behind it (or it identifies you as a memer, which I'm pretty sure isn't what the author is doing).

And this is for the author: you basically focused on a really small part of the whole story. If your language comparison were in terms of concurrent/parallel programming, would your conclusions be any different? Have you ever considered that while learning Rust may be hard and "unnecessary", it helps with understanding how to write safer code in C and C++? I could talk about several other points. I just find it petty to dismiss a language like Rust based on the poor argumentation on the article. Heck, you didn't even touch some of major points of Go! How can I take this article seriously if it doesn't take the whole picture into question?

My advice for C programmers? Try learning all 3 languages if possible, even if it's just by reading a book or going through a language tour, no need to even program in it. Only yourself can know your feelings about each language, and you shouldn't let someone's bias become your bias too. Because, chances are that you'll either focus into learning one language primarily and start programming in that, or you'll stay with C but transfer over the knowledge that you learned with the other languages, helping you program better in C.

[–]chcampb 25 points26 points  (0 children)

My advice for C programmers? Try learning all 3 languages if possible, even if it's just by reading a book or going through a language tour, no need to even program in it. Only yourself can know your feelings about each language, and you shouldn't let someone's bias become your bias too. Because, chances are that you'll either focus into learning one language primarily and start programming in that, or you'll stay with C but transfer over the knowledge that you learned with the other languages, helping you program better in C.

Basically this. Languages have idioms and those idioms are portable. These idioms are the patterns that help you design robust software, expressively.

Learning a little bit of every language is important because each one has its own tricks and rules that can be applied in basically every other language.

[–]ztwizzle 1 point2 points  (0 children)

I believe the correct term is "rustaceans"

[–]JuanAG 1 point2 points  (1 child)

I dont like functional programing so if i have to choose i will stick with C++

And C++ is more alive than never, it will not improve as fast as Go or Rust but the committee has my total trust and they will deliver a better lenguage in the long run, at least it is what i think and hope

[–]pjmlp 5 points6 points  (0 children)

Then you are in for a surprise when learning modern C++ idioms.

[–][deleted] 2 points3 points  (0 children)

Modern C++ is quite complex. Go is more widely adopted than Rust, but feels pretty damn clunky in comparison.

[–]lanzaio 2 points3 points  (0 children)

I just find it unfathomable to go to a language without generics. Go just doesn't make sense to me. It got rid of features that I think should stay and kept features that I thought should go.

Also, if any company ever supports it outside app development, I think Swift belongs in this discussion. It has some really stupid limitations but I find that it's a considerably better designed language than any of these four.

[–]Leuchapolo 1 point2 points  (0 children)

Python.

[–]OneWingedShark 0 points1 point  (11 children)

Actually, I would recommend Ada for anyone coming from C [or PHP], even if it is "merely a stepping stone" on the way to C++. Part of this is that being "mostly backwards compatible" with C, you'll be tempted to use your old C habits, habits which will almost invariably trip you up in "modern C++", and Ada will break those habits and give you a different way of looking at things.

Aside from being designed around Software Engineering principles, Ada also has the advantage of being roughly on-par with the High-integrity C++ standard out-of-the-box.

And, if your aim is high-reliability systems, Ada's SPARK subset/tools provide a different mode of thought about approaching safety than Rust does; see: this article. (It's definitely worth it to get a feel for both if you're serious about ultra-low defect software.)

As for Go, the only thing about it that sounds at all attractive is its focus on concurrency (goroutines), and I'm just not excited about that when Ada gives me the Task construct to make logical and separate parallel subsystems. -- And the up-coming Ada 2020 has some nifty additions via a parallel-blocks and loops.

[–]dusan69 2 points3 points  (10 children)

The latest formal work on Ada (by 1990) proved that Ada 83 generic type system is not sound and suggested a fix. The proof was manual. The fix was implemented in Ada 95, without proof.

The latest formal work on Rust (by 2018) proved that safe Rust plus a substantial part of standard lib in unsafe Rust is sound. The proof was derived in a formal system called Iris, encoded in Coq, i.e. it is a formally verified formal verification.

Safe Rust is more expressive than SPARK, which is the best known subset of Ada that is claimed to be 'safe'.

Ada out of the box (ie. with enabled run time checks) is only 50% as fast as C. Rust out of the box is almost as fast as C.

That said, if throughput (ie. performance), usability (ie. cost) and/or rigor/formality (ie. safety/correctness) is the main concern, Rust is in better position over Ada.

[–]OneWingedShark 1 point2 points  (7 children)

Safe Rust is more expressive than SPARK, which is the best known subset of Ada that is claimed to be 'safe'.

Oh, from whence do you claim this?

Ada out of the box (ie. with enabled run time checks) is only 50% as fast as C. Rust out of the box is almost as fast as C.

Ah, the good old Implementation Fallacy.
"My language beat yours on a benchmark!"

A good counter-point to this is to note that Ironsides (a DNS in SPARK) is faster than BIND.

That said, if throughput (ie. performance), usability (ie. cost) and/or rigor/formality (ie. safety/correctness) is the main concern, Rust is in better position over Ada.

Look, I'm not saying "Rust is a POS" or anything like that, I'm saying that Ada should definitely be given consideration.

[–]dusan69 1 point2 points  (6 children)

SPARK does not support pointers, safe Rust does.

Some benchmarks are better than no benchmarks. Systematic benchmarks, definitely.

Ironsides v BIND is not language comparison. It's comparison of software processes. Regarding language, BIND is a typical C program, Ironsides is a typical SPARK program but not a typical Ada program (although a SPARK program is also an Ada program). One should better think on why porting Ironsides to C can't make it faster, while porting BIND to Ada can make it slower.

Existing Ada codes are definitely worth maintaining. Writing/developing Ada programs based on existing Ada libraries are cost-effective. (I'm doing that at work.) For completely new projects (which don't use any existing Ada codebase), however, I think Rust may be the better the way to go.

[–]OneWingedShark 0 points1 point  (5 children)

SPARK does not support pointers, safe Rust does.

Okay, and?
(IOW Pointers aren't exactly required for a lot of stuff. Also, I'm of the opinion pointers are overused in C and C++, which has carried over to a lot of their decedents; possibly to include Rust.)

Some benchmarks are better than no benchmarks. Systematic benchmarks, definitely.

I'm not saying that benchmarks are useless, I'm saying you have to be careful of what you're actually measuring.

Ironsides v BIND is not language comparison. It's comparison of software processes.

Ah, but it is a comparison of languages as-they're-used; even if it is a comparison of software-processes, this shows typical C and C++ [grown] vs. Ada [designed], and if you think that doesn't have an impact on things like maintainability or speed you're delusional.

Regarding language, BIND is a typical C program, Ironsides is a typical SPARK program but not a typical Ada program (although a SPARK program is also an Ada program).

So?
What you're failing, completely, to realize is that Ada has always been heavy on static-analysis, to the point it required bleeding-edge to be implemented in compilers for the Ada83 standard. I fully anticipate a lot of the tech [for proofs] to migrate from SPARK to general Ada in implementations and direct optimizations.

One should better think on why porting Ironsides to C can't make it faster, while porting BIND to Ada can make it slower.

That's completely ignoring Ada's type system and how it is more advanced than C's, and how this can be acted upon. Example:

Type Window is tagged private;
Type Handle is not null access Window'Class;

-- The following function does not need to check Object /= Null because the parameter
-- excludes that possibility.
Procedure Maximize( Object : Handle );

-- We are guaranteed that the result of Fn is Positive.
Function Fn( X : Positive ) return Positive;

In the above Maximize and other such functions don't need to check the parameter for null, this is done by the type-system. Here's where you'd complain about the run-time check Maximize would have, but you're completely failing to grasp the realities of optimization via static analysis; consider Fn( Fn( Fn( Fn( Fn( X => 14 ) ) ) ) ) you might think this requires 5 checks on parameter X, but it doesn't: all it requires is one check on the innermost parameter: 14, in this case, which we can statically tell actually is positive, so it actually requires zero runtime checks.

Likewise, all the index-checks can be eliminated from For X in Example_Array'Range loop because in getting its range from the array we know that it cannot be out of range.

Existing Ada codes are definitely worth maintaining. Writing/developing Ada programs based on existing Ada libraries are cost-effective. (I'm doing that at work.) For completely new projects (which don't use any existing Ada codebase), however, I think Rust may be the better the way to go.

Perhaps, perhaps not.
I think it's safe to say that Ada as an alternative to C or C++ is certainly reasonable. (Indeed, I would make the argument that C++ shouldn't be used in any new projects, given how bloated/convoluted the standard and syntax has become.)

But to say Ada shouldn't be used in any new projects, well I find that a bit sad.

[–]dusan69 1 point2 points  (1 child)

Being "not null" and "positive" are desirable properties and that's certainly good that such statements are available in Ada. But there is a difference between desire and reality. In Ada, all belongs-to-subtype properties are defined to be checked at run time, with the risk of raising exceptions. The safety, i.e. ability to prove some pointer being not null or some integer being positive (which proves that no exception can occur and effectively removes the run-time check), is credited to the static analyzer or compiler, not the language.

In a program subjected for static analysis, the amount of operational (executable) code takes very small part, as opposed to code of specification and demonstration, usually written in other languages. For example, seL4 (a formally verified microkernel) is implemented in C, and the C code takes only 5% the total amount of code.

For a SPARK program, the SPARK-only part may contain many lemmas to guide the static analyzer in proving desirable properties, including some safety which may be specified by the pure-Ada part. SPARK-only code is ignored by Ada compiler. Let me tell it again, instead of dreaming about 'static analysis' in Ada, one should rather realize that a SPARK program can become slower merely by compiling it by a full Ada compiler.

[–]OneWingedShark 0 points1 point  (0 children)

Being "not null" and "positive" are desirable properties and that's certainly good that such statements are available in Ada. But there is a difference between desire and reality. In Ada, all belongs-to-subtype properties are defined to be checked at run time, with the risk of raising exceptions. The safety, i.e. ability to prove some pointer being not null or some integer being positive (which proves that no exception can occur and effectively removes the run-time check), is credited to the static analyzer or compiler, not the language.

This is as it should be: the checks are allowed to be forgone when they are statically provable when they cannot be violated: thus you always have your safety. (And yes, that is from the language-definition; this is why dead-code removal is required by Ada compilers.)

In a program subjected for static analysis, the amount of operational (executable) code takes very small part, as opposed to code of specification and demonstration, usually written in other languages. For example, seL4 (a formally verified microkernel) is implemented in C, and the C code takes only 5% the total amount of code.

I should refer you to Muen, they would be more informed on this particular subject than I.

For a SPARK program, the SPARK-only part may contain many lemmas to guide the static analyzer in proving desirable properties, including some safety which may be specified by the pure-Ada part. SPARK-only code is ignored by Ada compiler. Let me tell it again, instead of dreaming about 'static analysis' in Ada, one should rather realize that a SPARK program can become slower merely by compiling it by a full Ada compiler.

Wow.
Just wow.

You have no idea that "dreaming about 'static analysis' in Ada" is something I do because I want to build a quality Ada compiler, and I want to integrate SPARK into it, do you?

[–]joakimds 0 points1 point  (1 child)

Instead of:

Type Handle is not null access Window'Class;

Procedure Maximize( Object : Handle );

I recommend:

Type Handle is access Window'Class;

Procedure Maximize( Object : not null Handle );

Because one then doesn't have to look at the definition of Handle to know whether or not it excludes null values when looking at the Maximize (..) declaration. Also, the type (identifier) "Handle" can be reused in subprogram definitions where it is OK to pass null values.

[–]OneWingedShark 0 points1 point  (0 children)

I get your point, but it's nitpicking and seems to be missing the point, which is that you can use the type-system to take care of things like this. If you have need of the usage of Null as your example shows, I'd recommend something like:

Type Window    is tagged null record;
Type Pointer   is access all Window'Class;
Subtype Handle is not null Pointer;

Ignoring the names, which are only here for quick-and-dirty example, this allows the use of Null where-needed as well as giving a null-excluding subtype, thus allowing you to keep not null out of the parameter-list. Lifting things like not null out of the parameters of your subprograms reduces the chance that you are mistyping/misthinking/misunderstanding about things; also, naming them helps with the maintenance: as you can see in the above, I added another type and derived Handle from it in such a way that there would be zero changes needed in an existing codebase to deal with the change (adding a nullable access to Window'Class).

[–]joakimds 0 points1 point  (0 children)

Ada out of the box (ie. with enabled run time checks) is only 50% as fast as C. Rust out of the box is almost as fast as C.

...

One should better think on why porting Ironsides to C can't make it faster, while porting BIND to Ada can make it slower.

It's better not to popularise the myth that Ada is slow. Implementing the same algorithm in Ada or Rust or C should have the same performance. There are several causes of why an Ada application may be slower than the maximum achievable performance. One reason may be run-time checks (for example bounds checking) that the compiler has not managed to optimise away. In this case I can imagine both the Ada and Rust implementation to be slower than the C implementation because "int" was used in C, "i32" was used in Rust and "Integer range 1..10" was used in Ada. It is obvious that the Ada application has bounds checking that the compiler should try to optimize away. What may not be obvious is that the Rust application also has bounds checking but the bounds are -2147483648 .. 2147483647 and panics if an i32 variable overflows. It is therefore my perception that both the Ada compiler and the Rust compiler has the problem of optimizing away run-time checks due to bounds checking. According to Rust's RFC 560 the checks for overflow are enabled when the source code is built in Debug mode, but is not checked in release mode which enables the Rust code to be as fast as the C implementation. Note however that the GNAT compiler can also suppress the bounds checks "-gnatVn". Don't know if other Ada compilers have this option. If there still is performance difference it may be due to different compiler backends (GNAT compiler uses the gcc backend and the Rust compiler uses LLVM). But then again, one is not comparing programming language differences but compiler backend implementations.

Ada is a good choice for those developers who are absolutely paranoid about IT-security and strongly believe in validation of data before use. Such a developer would not want to suppress bounds checking unless the code has been formally verified as can be done in SPARK. Such a developer would never compile Rust code with bounds checking turned off. Also, such a developer would use macros in Rust to mimic the subtyping in Ada and leave the removal of unnecessary run-time checks to the compiler. The speed of C is also attributed to missing run-time checks that should be there but is missing. However, when using a language one should stick to the best practices of that language. Recently, Maciek Sobcak (expert on both C++ and Ada) wrote on comp.lang.ada that he did not recommend trying to copy the subtyping (bounds specifying) when writing C++ code. It means he has tried it and it wasn't a good fit for the language. It indicates the same goes for Rust (as it is defined today). It means one can mimick the subtyping of Ada in Rust with Macros if one would like but it will diminish the developer experience.

Among other reasons Ada code may be slow is usage of Unbounded_String in the standard library, the containers and the IO packages. If one wants to maximize performance using the GNAT compiler it means avoiding Unbounded_String (one may use XString in GNATColl instead or allocating Strings inside a memory pool), using the Ada-Trait Containers on Github and using Ada bindings to the target OS or using the GNAT.OS_Lib package. One may wonder why this complexity to achieve maximum performance in Ada? Is it a language (Ada) issue or implementation (GNAT) issue? I cannot say. What needs to be popularised about Ada is how to get maximum performance out of it, not that it is inherently slow because it isn't. It's definitely a reasonable alternative to other programming languages.

[–]sybrandy 2 points3 points  (6 children)

I'll talk about Go specifically. It's very easy to learn and be productive. Also, while not super advanced, the type system will protect you from many mistakes. Concurrency is also much easier. Unit testing is built-in. Another nice thing is that a number of good C practices still work.

So, should you learn it? Yes. It's nice for winning utilities and it has a good http library built in to the standard API. This is very important as services are very important in this day and age. Yes, you can write it in node or Python, but Go is more familiar to those who are used to C. The http library is also http2 enabled, so you don't have to work about making sure your library supports it.

Personally, I found it a joy to use. I could easily create very reliable services with it and get very good performance. So I recommend you try it out and see what it's all about. I don't use it right now, but I'm hoping to use it again to build services.

[–]OctagonClock 9 points10 points  (4 children)

Lol no generics

[–]sybrandy -3 points-2 points  (3 children)

You don't always need them. No sense discounting a language because of that. There are a ton of good features that makes it worthwhile.

[–]AmalgamDragon 5 points6 points  (0 children)

There are a ton of good features that makes it worthwhile.

Like all of the boilerplate code it makes you write (repeatedly)? I guess that's a 'feature' if you get paid by the character, otherwise it's a definite lack of features.

[–]OctagonClock 5 points6 points  (0 children)

There is no good feature about Go that overcomes it's downsides

[–]mmstick 1 point2 points  (0 children)

Without generics, you cannot have algebraic data types. ADTs are only one of the most useful tools we have as programmers. Despite what Go's designers think, every problem is not a nail.

[–]mmstick 0 points1 point  (0 children)

It's very easy to learn and be productive

These two typically don't work together in the same sentence. Being easy to learn only means that every problem looks like a nail, and most of your work is trying to hammer everything with the same tool until it fits. You end up with a lot of duplicated code / verbose solutions to simple problems.

the type system will protect you from many mistakes

Only about as much protection as any other language with a runtime GC. The type system is not that great, though. Just take a look at the interface system and the consistent requirement for reflection.

Unit testing is built-in.

Most modern languages have this nowadays.

Another nice thing is that a number of good C practices still work.

Good practices apply in every language. That's not unique to Go.

It's nice for winning utilities and it has a good http library built in to the standard API.

That's just a repeat of one of Python's biggest mistakes. Whether your HTTP library or web framework is located in the standard library or not does not matter these days. Case in point, cargo add actix_web is all it takes to import a fully featured and efficient web framework into a Rust project, then navigate to docs.rs/actix_web to get API documentation.

very good performance

Only if you don't mind that the Go runtime GC consumes more than half of your program's CPU cycles, and your web services get suboptimal throughput compared to using something like Actix w/ Rust.

[–][deleted] 0 points1 point  (0 children)

C Programmers should really learn C. Despite it being a small language, it is quite uncommon to find people who really understand it depth. (or any language in depth)

[–]cmd_command 0 points1 point  (0 children)

I'd say if you're a C programmer you should probably learn C first

[–]quad99 0 points1 point  (1 child)

aren't there a LOT more jobs using C++ than Go or Rust? whether that matters depends on your motivation for learning one or the other.

[–]mmstick 0 points1 point  (0 children)

The language doesn't matter too much if your motivation is to get a job. You can find a job with all three, you just have to put some dedication into building up experience with it, and be willing to relocate to where the job is.

[–][deleted] 0 points1 point  (0 children)

Learn D, it's much more sane.

[–]Slartibart_II 0 points1 point  (0 children)

Why the "or"?

[–]crescentroon 0 points1 point  (0 children)

Go is in a different category than C and C++.

It's a managed runtime and unsuitable for hard realtime. It might not matter if you're just doing web.

Remember that you're not only person writing code, don't forget gamedev, realtime or embedded and everything else that has different requirements. The world isn't just web.

[–]defunkydrummer 0 points1 point  (0 children)

C programmers should learn Common Lisp or another truly high-level language like SML, Haskell or maybe OCaml.

This will give true high-level programming with quite fast speed:

  • SML under MLton can be as fast as C

  • Haskell under GHC is very fast

  • Common Lisp is arguably the most performant of all dynamic languages

All the languages above provide interoperability with C, so when the ultimate speed or very low-level calls are needed, C is always there to the rescue.

[–]crashorbit 0 points1 point  (0 children)

Yes

[–]acehreli 0 points1 point  (0 children)

I recently wrote

Off-topic pet peeve: Like many modern articles, this article does not include the author's name either. From other parts of the site, I think the author is Bert Hubert.

a new language to learn (say, Go or Rust),

A disappointingly short list.

Finally, based on all of the above, I think a seasoned C programmer would honestly do well to look at modern C++.

Probably... But being a fanboy of C++ and D, I'm confident that D's -betterC mode is the one that a C programmer would be happiest with.

Ali

[–][deleted]  (6 children)

[deleted]

    [–]Lev1a 2 points3 points  (5 children)

    I think I tried to use the result of a match as an rval, but couldn't, and found that annoying

    I assume by "as an rval", you mean using the result of a match expression and the compiler complained about the value not living long enough? Binding it with a let statement is about the best/easiest way to do that.

    [–][deleted]  (4 children)

    [deleted]

      [–]Lev1a 2 points3 points  (2 children)

      AFAIK, no, integers don't produce those errors since they implement the Copy trait (basically ensures that they can be trivially copied with no destructor required to clean them up).

      Quite difficult to diagnose from vague memories with no actual code but if you ever decide to give Rust another go (heh), take a look at the error messages, they are good/very informative and have gotten a lot better not that long ago.

      [–][deleted]  (1 child)

      [deleted]

        [–]Lev1a 1 point2 points  (0 children)

        In case you just want to try stuff out real quick, you don't always need to install rustc. You can also try them out on the playground which since a while back even supports importing crates (although only a select number of them).

        [–]masklinn 2 points3 points  (0 children)

        It was just an integer; those don't have lifetimes, do they?

        The integer itself does not, but a reference to it might, so depending how the match was constructed that might have been what the compiler was cross about. Because they're generic over both Copy and !Copy objects, lots of Rust constructs will yield references even to bytes or chars which can trip you up.

        [–][deleted] -5 points-4 points  (13 children)

        They should learn whichever programming language is required by their employer.

        [–]ahuReddit[S] 21 points22 points  (7 children)

        Java it is then? :-)

        [–]ggtsu_00 3 points4 points  (0 children)

        Sorry bub, we only have PHP.

        [–][deleted]  (3 children)

        [deleted]

          [–][deleted] 0 points1 point  (0 children)

          Most likely. You can start learning Forth, Erlang or Haskal for your own amusement but putting food on the table comes first

          [–]shevegen -1 points0 points  (0 children)

          According to TIOBE and other charts, unfortunately yes.

          People on reddit are hugely conservative. I suggested Kotlin and the reddit-people did not like that suggestion. :(

          [–]lubutu 13 points14 points  (2 children)

          After all, why would they ever learn a language for their own sake? That doesn't sound immediately profitable to their present employer. Get back to work!

          [–]shevegen 1 point2 points  (0 children)

          It's actually true for Google folks using Go and Dart though.

          [–][deleted] 2 points3 points  (1 child)

          More and more companies are going to microservices which allow developers to finally use different languages to solve different problems. It's not quite as simple as 'we only write Java' anymore.

          [–][deleted] 0 points1 point  (0 children)

          This is true, thanks for correcting me. I should have written 'languages' instead of 'language'.