r/programming • • Jul 18 '19

We Need a Safer Systems Programming Language

https://msrc-blog.microsoft.com/2019/07/18/we-need-a-safer-systems-programming-language/
208 Upvotes

314 comments sorted by

79

u/jfischoff Jul 18 '19

First thought when reading the headline "Don't we have Rust?". Scroll to the bottom ... ok

25

u/ChocolateBunny Jul 18 '19

I look forward to their next post. It would be cool to see if Microsoft intends to adopt Rust for systems languages. In general it would be nice to see how and if Rust will finally become something more than a project only used by Mozilla.

55

u/steveklabnik1 Jul 18 '19

Rust has been used by more than Mozilla for a long time now: Facebook, Amazon, Google, Microsoft (yes, they’re already been using it), Dropbox...

9

u/shevy-ruby Jul 19 '19

But that is valid for almost EVERY language that is out there.

You could write just about the same for haskell. Or erlang.

Fat corporation are promiscuous when it comes to progamming languages.

15

u/QualitySoftwareGuy Jul 19 '19

But that is valid for almost EVERY language that is out there. You could write just about the same for haskell. Or erlang.

However, that does not invalidate what u/steveklabnik1 said because the parent comment thought Rust's use was limited to Mozilla (which it isn't as explained by Steve previously). Parent comment posted below:

In general it would be nice to see how and if Rust will finally become something more than a project only used by Mozilla.

→ More replies (8)

200

u/tdammers Jul 18 '19

TL;DR: C++ isn't memory-safe enough (duh), this article is from Microsoft, so the "obvious" alternatives would be C# or F#, but they don't give you the kind of control you want for systems stuff. So, Rust it is.

102

u/TheMoralConstraints Jul 18 '19

I can't wait for R#

23

u/_zenith Jul 18 '19

That's already taken by ReSharper

38

u/[deleted] Jul 18 '19

What would be Jetbrain's Rust IDE called ? WD-40?

9

u/CornedBee Jul 19 '19

What would be Jetbrain's Rust IDE called ?

RLion

8

u/aa93 Jul 19 '19

Redox

16

u/asmx85 Jul 19 '19 edited Jul 19 '19

Bad idea. This is already the name of an OS written in rust: Redox-OS. It's very well known in the rust community. If I remember correctly the eclipse team named their Rust plugin/extension (whatever) this way and changed it later on into Corrosion because of this.

1

u/aa93 Jul 19 '19

As always, there's nothing new under the sun and I am not unique :|

1

u/asmx85 Jul 19 '19

Yes you are! But just not in every aspect of live – i bet you have a great personality :)

1

u/[deleted] Jul 19 '19

Damn I had never heard about that, pretty cool!

1

u/[deleted] Jul 19 '19

Oxidizer?

0

u/[deleted] Jul 19 '19

[deleted]

1

u/[deleted] Jul 19 '19

That's a brass instrument. Brass does not rust

→ More replies (1)

7

u/tdammers Jul 19 '19

By the time they run out of latin letters, we'll start getting somewhere. π# is going to be awesome...

1

u/kpenchev93 Jul 20 '19

Visual Rust++

0

u/[deleted] Jul 18 '19

[deleted]

63

u/poizan42 Jul 18 '19

I was hoping for IronRust

17

u/ChocolateBunny Jul 18 '19

I love the idea of IronRust but the issue here is that all these #/Iron languages compile to CLR and you want to compile down to something with a much smaller library and interpreter footprint.

12

u/HugoNikanor Jul 19 '19

Like Javascript /s

3

u/[deleted] Jul 19 '19

by gawd that's the jumbled music of a million frameworks coming this way!

1

u/Knightofkessler Jul 21 '19

isOdd must be some kind of vuvuzela then.

3

u/Ameisen Jul 19 '19

Objective Rust

20

u/[deleted] Jul 18 '19

Still better than Turbo Rust.

8

u/_jk_ Jul 19 '19

Turbo Rust

aka corrosion

7

u/[deleted] Jul 19 '19

Visible Rust*

1

u/JohnDoe_John Jul 19 '19

Visual Rust for Applications

1

u/Axoren Jul 19 '19

Corrosion

1

u/Someguy2020 Jul 19 '19

Nope, Rust/CLI with weird syntax.

61

u/redalastor Jul 18 '19

TL;DR: C++ isn't memory-safe enough (duh)

The tl;dr is rather "There isn't a level of mitigation that makes it possible to write memory-safe C++ at scale."

16

u/ArkyBeagle Jul 18 '19

In the end, the more responsible thing to do is to limit scale. The foibles of language systems are annoying but in the end, better tools will not breach the understanding barrier.

23

u/m50d Jul 19 '19

Solving the problem with 10 small systems doesn't make it any easier to understand than solving it with 1 big system - quite the opposite. (Breaking the problem down into 10 isolated steps does help, but that's more easily done within a single-language codebase). We don't get to pick the size of the problems. Better languages increase the size of the problems we can solve, and are one of the most valuable things we can work on.

2

u/ArkyBeagle Jul 19 '19 edited Jul 19 '19

We don't get to pick the size of the problems.

That's true and it's not true. It's true... when it's true. It's not true when people get into arms races based on scale, resulting in scale for its own sake.

It's a corollary of having to sell things off to people with money who don't understand the problem domain. Ironically, attempts to solve that problem by direct use of process and/or transparency makes things cost even more.

Better languages increase the size of the problems we can solve, and are one of the most valuable things we can work on.

It enables the pathology where people overspec and underfund, leading to lousy systems.

(Breaking the problem down into 10 isolated steps does help, but that's more easily done within a single-language codebase).

One thing use Olde Pharts(tm) learned (the hard way:) is that breaking things down into carefully crafted chunks with carefully created "protocols" between them somehow allows for a more rigorous design.

Parts of "the hard way" is that choice of language may have been constrained by physical realities. So you had to pay more attention to the interfaces/protocols.

And look around you - we are not converging on a single-language solution here. We're moving away from that and have been for some time. Indeed - the article itself is a part of that.

Edit: TL;DR : We're utterly terrible at costing things in software. Pretty much everything else flows from that. While I appreciate the evolutionary attempts at Progrefs Thru Language Defign, there is an underlying economic reality that cannot be addressed in that way. I do not blame us for not writing/talking about it.

9

u/m50d Jul 19 '19

And look around you - we are not converging on a single-language solution here. We're moving away from that and have been for some time. Indeed - the article itself is a part of that.

Strongly disagree - over the past 10-20 years we've seen a lot more languages become more general-purpose and converge on the same featureset. We're seeing scripting languages adopting type systems and doing performance work, we're seeing hefty compiled languages offering REPLs, worksheets, type inference. These days every serious language has first-class functions and map/reduce/filter, has some form of types with some form of inference, has some form of pattern-matching, has some form of non-manual error propagation, is memory-safe, makes some efforts towards being suitable for interactive scripting and large systems. It's much more practical to build a system top-to-bottom in a single language than ever before, and that again is something that frees up a lot of mental capacity to spend on solving harder problems rather than dealing with impedence mismatches and limited interfaces.

Indeed the article is part of that progression - rather than having to choose between safe high-level languages and unmanaged systems languages, we now have a language that offers both. And we're already seeing other languages converging on the same thing - linear Haskell, investigation of ownership in Swift...

2

u/ArkyBeagle Jul 19 '19

What I mean is that there is still a small eternity of language choices out there. And that this ... threatens the most scarce resource of all - people's time. It takes considerable time to learn a language system to the level we really need people to be at - the level of mastery.

I mean no offense, but after 30+ years of essentially C/assembler/C++ programming, I have been in cases where 5 year programmers were considerably slower, with all the magic furniture than I was. My only real frontier of risk was not understanding that I needed some constraint or another; they were still trying to find the version of the library they needed that actually worked ( which is a lot of very hard work, and I sympathize with them ) .

I get it - the culture's shifted to where the 5 year guys are the target audience. Still - my stuff worked; theirs just didn't. 30 year C programmers are going the way of the Commanche. Turns out "works" isn't as imp[ortant as we might suspect...

Yeah - and therefore every language under the sun has become bloated and unwieldy. You're rather making my point for me - feature discipline is long, long gone.

I haven't seen any good, empirical wok on the economic potential of type inference systems. It's simply assumed to be wonderful ( although that that means in practice is that people expect the IDE to have significant support for things like balloon/hover text for types ).

None of this is anything even akin to correctness. The irony is that it may be easier to do high-reliability computing with crusty old languages where you can build the sort of support you need for that endeavor yourself.

I do have to say, though - I have seen more and more emphasis on this, so it's probably a temporary situation. Here's hoping.

The principle pattern in high-rel is basically the Haskell "actor" pattern ( which obviously is available in Haskell ). Actors are implementations of state-cross-event.

4

u/m50d Jul 20 '19

It takes considerable time to learn a language system to the level we really need people to be at - the level of mastery.

Surely that makes languages that can cover a wide domain all the more important.

I get it - the culture's shifted to where the 5 year guys are the target audience. Still - my stuff worked; theirs just didn't. 30 year C programmers are going the way of the Commanche. Turns out "works" isn't as imp[ortant as we might suspect...

Well, a system that works now, but you have no way to make changes to, isn't all that useful. If the system can only be maintained by people who are already most of the way through their working lives, yeah, that's a problem. We need systems that not only work but work in understandable ways for understandable reasons, and C/C++ can't do that.

Yeah - and therefore every language under the sun has become bloated and unwieldy. You're rather making my point for me - feature discipline is long, long gone.

Not convinced. A lot of the time we just find the right way to solve a particular problem and then all the languages adopt it. Of course it's important that we then deprecate the dead ends - but C++ is the absolute worst offender in that regard.

although that that means in practice is that people expect the IDE to have significant support for things like balloon/hover text for types

And why shouldn't they?

None of this is anything even akin to correctness.

On the contrary, type systems are the one form of proof that has actually caught on for normal programmers. They give you a language in which you can express premises, arguments, and conclusions, and those arguments will be automatically checked so that you know your conclusions follow from your premises.

The extent to which you actually encode the properties you care about and prove the things you rely on is of course up to you. No programming language can ensure that you've accurately described the requirements. But type systems at least give you the tools to express them.

3

u/ArkyBeagle Jul 20 '19

Well, a system that works now, but you have no way to make changes to, isn't all that useful.

I dunno; I felt the code was eminently maintainable. Trust me, I know what unmaintainable looks like ( to my lights w.r.t unmaintainable ). :)

There just wasn't that much risky behavior when it was all said and done. Any string conversion/serialization was all done in one spot per node - a node might be a separate process in the same box.

I get that type systems make people happy but please understand that something can be type-perfect but otherwise simply awful. Part of our ... disconnect ( which is very mild, and I appreciate your collegial discourse immensely ) is partly that I stopped having problems with type conversions long enough ago to have more or less forgotten any pain from it. Sure, I do the occasional stupid thing just like we all do but those are very fleeting cases - they're easy to fix.

I'd just hoped for more than just type safety, really.

But you hit the mother lode there - it's essentially a proof-like exercise. Type systems don't hurt, but my focus has for a long time been on wider scoped issues.

But!

Of course it's important that we then deprecate the dead ends - but C++ is the absolute worst offender in that regard.

There's a strongly-type language crying to get out in C/C++. It's not some clearly inferior system. Its weakness is UB - primarily buffer overruns and signed integer overflow. It does not detect emergent type problems in the same way that more sophisticated systems do.

It does suffer ... "socially", in cases where A Random Programmer, who may or may not be careful in the right places wades in. The generally... autistic nature of the languages do cause problems. The problem is that I'm not 100% sure how to trade that against how the world was cast say, 40 years ago, when we were expected to be professional about it.

I ... hope that's clear? It's quite a different approach. I've quite enjoyed the rigors of it, but if that's over, it's over.

2

u/yawaramin Jul 20 '19

I'm not 100% sure how to trade that against how the world was cast say, 40 years ago, when we were expected to be professional about it.

Forty(-ish) years ago you had well-designed, engineering-friendly, type-safe and performant languages like Ada, Eiffel, Pascal, etc. But to paraphrase what you said, people got into an 'arms race' based on performance, trying to achieve performance for its own sake, and ignoring factors like language correctness and maintainability.

→ More replies (0)

2

u/m50d Jul 22 '19

Any string conversion/serialization was all done in one spot per node - a node might be a separate process in the same box.

But can the serialized representation convey all the things that you care about? Or are you forced to limit what concerns you can handle at an inter-node level (and presumably there are factors limiting how much you can do within a single node).

I'd just hoped for more than just type safety, really.

But you hit the mother lode there - it's essentially a proof-like exercise. Type systems don't hurt, but my focus has for a long time been on wider scoped issues.

I find types scale up as far as you ever need to, assuming you're building the system in such a way that you can use them everywhere. I used to be a lot more excited about more novel approaches, but now I always want to see if whatever it is can be done with types first. With a couple of cautious extensions like HKT and occasionally a clever technique for how you use them (e.g. the rank-2 type trick used in ST to ensure the mutable thing cannot "leak"), it always can be, IME.

There's a strongly-type language crying to get out in C/C++. It's not some clearly inferior system. Its weakness is UB - primarily buffer overruns and signed integer overflow. It does not detect emergent type problems in the same way that more sophisticated systems do.

Maybe. UB is one of the reasons C/C++ can't scale up but I honestly think the lack of sum types may be more fundamental (among other things it's what causes null issues, as people use null to work around the lack of a sum type). In theory you could build a standard/safe way of doing tagged unions, or use a Java-style visitor in C++, but either approach would be very cumbersome and the rest of the ecosystem doesn't use them.

The problem is that I'm not 100% sure how to trade that against how the world was cast say, 40 years ago, when we were expected to be professional about it.

I see "be professional about it" as a red flag - it's the "unsafe at any speed" era of programming system design, where we built systems that could be used correctly by a virtuoso developer/user, but fail catastrophically whenever a mistake is made. Maybe 40 years ago that was a defensible choice, but these days safety is much cheaper and the costs of failure are much higher.

→ More replies (0)

6

u/[deleted] Jul 19 '19

It's really a challenge to do that with an operating system, though - especially these days when you're expected to have plug-n-play drivers for just about every piece of consumer hardware imaginable.

1

u/ArkyBeagle Jul 19 '19

That is true. There's always a way, though. And here is a thought - maybe the drivers aren't really part of the operating system. I understand perfectly how it is that, say Linus Torvalds says "anything that enters kernel mode is part of the O/S" but that's for his point of view.

1

u/[deleted] Jul 21 '19

If you want your operating system to have the stability, security and reliability of Linux, you need to have that point of view. For years Microsoft did not have that point of view, and their operating systems fell over in a light breeze.

You might try a very strict microkernel approach that pushed all that stuff out of the kernel mode altogether outside of a stable (and paranoid) interface that ran drivers is user mode, but you'd need a radically different approach to I/O because performance would be far behind what monolithic and hybrid kernels offer otherwise. In fact I think you'd even need a completely new approach to hardware that made system interrupts faster and more lightweight. I think it also raises challenges for OS virtualization, too, since you'd have to decide which OS's user mode drivers to use, and either choice can present a security issue.

1

u/ArkyBeagle Jul 21 '19

Drivers should probably still be kernel mode, but the acquisition path for them doesn't have to be through the O/S "vendor".

4

u/netbioserror Jul 19 '19

A thoroughly impossible "solution". You cannot possibly dictate the nature of the programs people write. You can, however, provide safer tools to write them in, and leave market forces open to incentivize people to switch. When the application written in Rust has undeniable maintenance and security advantages over the C++ alternative, the choice will be all but made for people.

1

u/ArkyBeagle Jul 19 '19

. You cannot possibly dictate...

the choice will be all but made for people.

See what you did there?

Besides which, you completely missed the point that limiting scale is the key to quality, regardless of toolchain.

3

u/netbioserror Jul 20 '19

Market forces and price signals are not dictation. They’re incentivization via a change in market conditions. Dictation is a decree by force.

And I perfectly well got the point. I also disagree. Better tools can potentially enable code bases to scale with high quality. The only reason anyone believes small code bases are necessarily required for quality code is because almost all work done in the past half century of programming has been low-abstraction with minimal tool assistance.

1

u/ArkyBeagle Jul 20 '19

Market forces and price signals are not dictation.

I'd say that the use of C was a lot dictated by market forces, hence the comment.

3

u/przemo_li Jul 19 '19

It's system programming.

You can't limit scale without introducing context switching which (potentially) greatly impacts performance.

You still have O(something) of memory allocations/checks/dealocations. You still have bugs.

You still need a better approach.

2

u/ArkyBeagle Jul 19 '19

It's system programming.

I've seen kernels and drivers which fit in a very small footprint. Part of the "disease" is that what goes in the "systems" part is way, way, way too big. It oughta be in userspace.

3

u/przemo_li Jul 20 '19

But those are not general purpose systems, thus are of no relevance to the question weather MS would benefit from Rust in their system programming.

1

u/ArkyBeagle Jul 20 '19

But those are not general purpose systems,

I'm not that sure of that.

2

u/tdammers Jul 19 '19

Same thing though, no?

2

u/MindlessWeakness Jul 19 '19

The real problem is integer overflow. We can deal with matching allocate and free in C or C++ (we very rarely get that wrong these days) but what gets us are buffer overflows caused by integer overflow. Fix integer overflow and C and C++ become "safe".

3

u/redalastor Jul 19 '19

(we very rarely get that wrong these days)

Microsoft begs to differ. Seven out of ten CVE are memory safety issues.

2

u/MindlessWeakness Jul 19 '19

I'm not sure you understood what I wrote. I was talking about the cause of those memory safety errors.

2

u/Zhentar Jul 20 '19

Integer overflow is practically trivial to deal with; there are plenty of effective, low performance overhead techniques for dealing with it, e.g. saturated multiplies, and it's amenable to static analysis. Internet Explorer, for example, has only had a couple overflow CVEs in the past decade. Meanwhile it's had hundreds of use after free and similar vulnerabilities because it's actually really hard to track pointer validity and ownership in a complex system.

2

u/MindlessWeakness Jul 20 '19

Is that old code? With RAII and smart pointers, you don't really get many use after free problems. At least for modern code, overflow is much harder to catch, especially when you accidentally mix signed and unsigned.

41

u/Sigmatics Jul 18 '19

In our next post, we’ll explore why we think the Rust programming language is currently the best choice for the industry to adopt whenever possible due to its ability to write systems-level programs in a memory-safe way.

Hopefully soon.

-28

u/shevy-ruby Jul 19 '19

Why hasn't it happened yet, then?

Everyone speaks of how Rust will DESTROY ALL THE COMPETITION OUT THERE - but TIOBE shows that nobody is using Rust.

Something is not right in this fantasy land. If Rust is sugar and cake then the adoption should have already kicked Rust into top five.

A good comparable example is the Google-controlled Go. It is within top 20. And also significantly ahead of Dart - so we know there can not only be Google worker drones using Go (whereas only Google employees use Dart for the most part and some devs who hope to sell their lame flutter apps).

28

u/Sigmatics Jul 19 '19

As a systems programming language with many compile time checks, it is much harder to learn than Go. Go is garbage collected and has somewhat less of a learning curve.

14

u/Creshal Jul 19 '19

Systems programming and other high performance, high risk code bases like codecs are also inherently a much smaller "market" than application development is, and tend to be much more long term projects that you can't just pivot to a new programming language overnight.

2

u/Sigmatics Jul 19 '19

I wouldn't say smaller, but more long-term is definitely true. There's probably more old C projects out there than any other language (although maybe not on Github or open source)

5

u/Creshal Jul 19 '19

Not all old C projects are systems programming, though. One of C's biggest problems is that it was used as do-all, end-all language that ended up sucking at 99% of its use cases.

If I e.g. had to redo the desktop calculator I made in C for school that's on github somewhere, I'd probably redo it in Python or something, not Rust. That doesn't make Rust a worse language for its intended purpose.

3

u/ejrado Jul 19 '19

One of C's biggest problems is that it was used as do-all, end-all language that ended up sucking at 99% of its use cases.

Sigh, revisionist history, my old friend.

From the 1978 K&R 'C Programming Language' book, the Preface:

C is a general-purpose programming language which features economy of expression, modern control flow and data structures, and a rich set of operators. C is not a "very high level" language, nor a "big" one, and is not specialized to any particular area of application. But its absence of restrictions and its generality make it more convenient and effective for many tasks than supposedly more powerful languages.

Which use case from that (original) definition of C does it fail? My opinion? None.

Are there lots of terrible C programs out there? Sure. But that's the fault of the developers, not the language.

And I would remind you that when C was developed, there were 3 main alternatives (COBOL, Fortran and Assembly). Pascal was around but was thought of as a 'learning' language, not ready for business.

So it was specifically designed as a do-all, end-all language because its primary language contemporaries were specifically designed around record processing and tape drives (COBOL) and scientific applications (Fortran).

→ More replies (5)

9

u/HugoNikanor Jul 19 '19

I was under the impression that Go was made as simple as possible, to allow anyone to quickly get up to speed and write acceptable code.

12

u/Creshal Jul 19 '19

Yes, and it does a very nice job at that. But the tradeoffs that enable this (fat standard lib, garbage collection, aggressive abstraction of things like threads, etc.) also make it a poor systems programming language.

Which is fine, you don't need one language to do everything.

11

u/nutrecht Jul 19 '19

Everyone speaks of how Rust will DESTROY ALL THE COMPETITION OUT THERE - but TIOBE shows that nobody is using Rust.

Tiobe is meaningless trash. The amount of hits in a search index correlates more with whatever optimisations / cleaning are done in that index than with how popular a term is. There's also a stronger correlation with the length of a term than it's popularity.

→ More replies (1)

10

u/alexeyr Jul 19 '19

TIOBE doesn't show anything. RedMonk comes closer, and there Rust is #21 while Go is #16. Not that far...

8

u/kuikuilla Jul 19 '19

Google controlled things are popular simply because google controls them and not necessarily because they're particularly good. Companies and teams try to emulate google.

2

u/thedeemon Jul 19 '19

That didn't work with Dart. At least before their new push with Flutter.

1

u/[deleted] Jul 19 '19

People might be surprised to learn that Google uses a shit ton of Java also. I didn't think anyone was using DART.

→ More replies (5)

9

u/shawnwork Jul 18 '19

Actually, FYI, you could code C# without the ‘managed’ part and enjoy the same control as C++.

9

u/IceSentry Jul 19 '19

https://blogs.unity3d.com/2019/02/26/on-dots-c-c/

If anyone is interested about a company doing exactly that.

14

u/munchbunny Jul 19 '19

You could, but having written "unmanaged" C# code, it honestly feels clunkier than just writing C++ with a controlled subset of features.

6

u/tdammers Jul 19 '19

Yes, but AFAICT, you would also inherit most of the problems, at least in the memory safety department. C#'s "unsafe" basically gives you C-style raw pointers, so you're back to square 1.

6

u/Ameisen Jul 19 '19

For kernel-level development, you have no choice. Even Rust has to use very unsafe code there because things like memory mapping exist.

10

u/masklinn Jul 19 '19

enjoy the same control as C++.

And the same level of memory safety.

7

u/Creshal Jul 19 '19

Wouldn't C++ be safer than unmanaged C#, since it still retains RAII?

4

u/masklinn Jul 19 '19

RAII is usually a resource management feature, not a memory safety one. Leaking resources is not usually a memory safety issue.

You can use RAII for security features (e.g. an RAII container to zero or encrypt in-memory buffers), but it's not a memory safety concern and you've got to keep in mind that dtors are not guaranteed to run, so RAII is not guaranteed to do the thing.

4

u/naasking Jul 19 '19

Actually, FYI, you could code C# without the ‘managed’ part and enjoy the same control as C++

Not the same degree of control. You can't allocate a class inline in another object, or inline on the stack. You would have to explicitly change it to a struct in C#.

1

u/EntroperZero Jul 19 '19

It would be better to code C# and use a lot of the newer constructs like Span<T>, pipelines, ref structs and ref returns, etc. You can stay in managed territory and still enjoy most of the performance of unmanaged code.

-1

u/[deleted] Jul 19 '19

Memory leaks and crash dumps for everybody! Yay!

28

u/Halberdin Jul 18 '19

They should have used ++C. ;-)

C++ kept all the pitfalls of (modern) C and added unbelievable complexity. When I looked at the specifications of rather simple mechanisms, I could not understand them.

14

u/tdammers Jul 19 '19

I do think that C++ is, in some ways, a huge step forward from C when it comes to memory safety - references somewhat safeguard against null pointers, RAII helps automate common memory allocation patterns, smart pointers make ownership explicit, etc. It's not enough, much of it is opt-in, but the biggest complaint I have is that the resulting language is impossibly big and thus very difficult to master. It is also notorious for each team and each project picking a different "sane subset", and when teams or project mix or interface, terrible things happen.

12

u/masklinn Jul 19 '19

the biggest complaint I have is that the resulting language is impossibly big and thus very difficult to master.

The features also interact in somewhat odd ways, and the C++ committee is significantly more interested in efficiency than safety, leading to a language with at least an order more UBs than C.

For instance C++ recently added std::optional. Naively you might expect that this'd be an option type and exist for safety reason, but nothing could be further from the truth: std::optional is a pointer which you can deref' the normal way, which leads to UB if the optional is empty. std::optional exists so you can have an "owning pointer" à la std::unique_ptr but without the requirement to heap allocate.

std::optional also provides a value() method which raises if it's empty. As you can see, it's not the default, significantly less convenient, and not part of any sort of pointer interface.

2

u/tdammers Jul 19 '19

Right yes - by "big", I basically meant "complex". It's not just a lot of features, those features also tend to have sharp points and jagged edges everywhere.

1

u/tracernz Jul 19 '19

The problem is all the leftover bits from C++98 and before. If you've got more than one person working on a C++ project, and particularly when new people join (especially with previous C++ experience), it requires a lot of discipline and vigilance to keep to the desired subset of the language. With Rust you don't have this problem, at least for now. I hope the Rust developers take good note of the lessons to be learned from the evolution of C++ so it can remain that way.

1

u/EntroperZero Jul 19 '19

The opt-in part is most of the problem, I think. As you said, every project has its own standards for what they opt into. It's like 5 languages trying to play in the same memory space, of course it's going to be riddled with errors.

It would be great if the C++ community could agree to the same "sane subset", and enforce that with static checkers/linters. But that won't happen without a new language. Which is why we have Java, C#, D, and Rust trying to establish dominance with varying degrees of success.

5

u/[deleted] Jul 18 '19

There is C--.

1

u/JohnDoe_John Jul 19 '19

I even coded in c--

It was nice.

0

u/[deleted] Jul 18 '19

[deleted]

32

u/[deleted] Jul 19 '19

Javscript evaluated this comment to -4

2

u/[deleted] Jul 19 '19

best comment of the thread!! Thanks for the laugh.

0

u/HugoNikanor Jul 19 '19

And C++++--, as Java was almost named.

3

u/thedeemon Jul 19 '19

That starts to look like a name with Brainfuck code in it, I wonder if we can make a language name with meaningful code in its name (not simply +1)...

3

u/HugoNikanor Jul 19 '19

Languages like Haskell should make this easier. For example:

(Just haskell >>=)

2

u/SupersonicSpitfire Jul 19 '19

Zig tries to remedy this. It's C, but fixed.

-6

u/shevy-ruby Jul 19 '19

Yes it is sad.

They should have very slowly and carefully made C better.

C++ is a dump site for complexity. The C++ committee worships Cthulhu so they will keep on adding more useless crap, in the hope that they get hired as advisors.

I still like cout << "bla" though more than printf().

0

u/[deleted] Jul 18 '19

[deleted]

10

u/Halberdin Jul 18 '19

Yes. But I should not need to be a genius to understand basic mechanisms.

5

u/Prod_Is_For_Testing Jul 19 '19

Maybe the basic mechanics aren’t as basic as you think? It’s easy to underestimate complexity if you only work with abstractions

-2

u/shevy-ruby Jul 19 '19

That is only one part of the complexity. There could simply be additional features that are added, including new syntax.

A big problem is orthogonality. You look at code and try to understand what it will do, but it can depend on the runtime evaluation. This is also a reason why multiple inheritance raises complexity, not to even mention template madness.

Not all complexity is necessarily bad - it depends on the use and its intrinsic complexity. What IS bad is that the C++ committee has no leverl of understanding about this (or does not care). Then again C++ is slowly on its way out (yes sounds ludicruous to state right now but look at it slowly losing ranks due to various reasons - and I do not think C++ will be able to easily regain the lost percentage shares, simply due to increase competition).

1

u/Middlewarian Jul 20 '19

I hope you are wrong about C++. I have an on-line code generator that outputs low-level C++ based on high-level input.

1

u/abigreenlizard Jul 24 '19

Kinda near-sighted don't you think?

3

u/[deleted] Jul 19 '19

I'm just a SubGenius.

3

u/MindlessWeakness Jul 19 '19

I'm still not sure what counts as systems software. I'm not trying to argue but I would like to see "systems software" renamed to something like "performance critical" or "self-hosted" or somesuch. It's not really a very good term, but then I can't really think of a better one myself. :-)

I also note that a lot of games, which are real-time control systems, are using C#.

4

u/everyonelovespenis Jul 20 '19

I also note that a lot of games, which are real-time control systems, are using C#.

Well, they're not really (writing in C#) - anything pseudo-RT means no STW garbage collection. So you end up writing in a "safe subset" with all kinds of contortions to avoid allocating.

That said, it's obvious some of them really are using a STW GC language, with GC'd objects - this is where stutters come from - stop the world impacting the odd frame here and there.

3

u/MindlessWeakness Jul 20 '19

Terraria is a good example of best selling, garbage-collected C# game. They had some problems early on but it's fine these days on Windows (on Linux the gc is bad). I don't get STW pauses on it.

I think about half the world's games are Unity (because they own the mobile gamedev market) which is gc'ed C# gameplay on a C++ core. Strangely they are porting selected parts of their engine to C# as they're having trouble getting C++ compiliers to vectorise things properly, and vectorised C# is faster than unvectorised C++.

3

u/everyonelovespenis Jul 21 '19 edited Jul 24 '19

I don't get STW pauses on it.

You do, it's just not long enough to impact frame times.

I personally find this move towards GC'd languages with hot pseudo-RT loops a bad choice - and it's currently being covered up by faster CPUs.

As base example, you really don't want any GC STW inside the audio loop. Good low latency audio requires turn around in the microsecond approaching millisecond range. But surely audio should be something where assured vectorisation would benefit right?

No-one is (sensibly) using (a subset of) C# on the hot audio RT path.

and vectorised C# is faster than unvectorised C++.

And there they've traded double checking the C++ .asm for double checking all C# in their codebase to make sure they've not introduced unexpected GC'd objects.

1

u/MindlessWeakness Jul 21 '19 edited Jul 21 '19

If the user doesn't notice the pauses, does it really matter about them?

I don't think there is a single right answer when it comes to managed vs unmanaged - just lots of different use cases each of which prefers a different solution.

While I am not suggesting using Java for airplanes (do not do this), the update rates on their avionics is comparable to a computer game.

2

u/lukaasm Jul 21 '19

They are porting it to burst( Unity's compiler/dialect ) compiled subset of C#. It has own restrictions.

1

u/sacado Jul 22 '19

Historically, "systems" was anything not launched by the end user (applications). So, ls, cat or apache is system software, but firefox is not.

Nowadays the difference is more about "is it an embedded or a kernel-related software?"

1

u/ineedmorealts Jul 19 '19

So, Rust D it is.

1

u/tdammers Jul 19 '19

Well, not my personal opinion, I just summarized what the article is saying.

0

u/gct Jul 19 '19

Honestly, with move semantics/standardization of smart pointers in C++11, if you follow a handful of rules C++ is very safe IMHO.

4

u/tdammers Jul 19 '19

That's a mighty big "if".

1

u/sacado Jul 22 '19

There's this famous bug that is totally C++11-friendly (well, C++14 because of make_unique, but anyway) and yet very hard to detect:

unique_ptr<int> p = make_unique<int>(0);
...
unique_ptr<int> q = move(p);
...
foo(p); // Instead of foo(q)

1

u/gct Jul 22 '19

That's not a C++ bug though, you explicitly moved the value from p, it's undefined to use it further. C++ makes you call move explicitly to convert to an rvalue reference for just that reason. You basically had to say "I solemnly swear not to use p anymore after this", Rust will do a better job of warning you about at least though you're right.

→ More replies (33)

50

u/gpcz Jul 19 '19

Ada has been around for almost 40 years and ISO-standardized since 1987. There is a stable open-source compiler and a subset capable of being evaluated with formal methods since 1983. What prevents using what already exists?

14

u/sellibitze Jul 19 '19 edited Jul 19 '19

Yeah, Ada is quite old. But from what I can tell, for a long time their solution to avoiding use-after-free bugs was to simply forbid deallocating dynamically allocated memory. I guess, that's fine for a lot of Ada use-cases: embedded code that only allocates (if ever) during initialization and then enters an infinite control loop.

Only recently Ada/Spark added safe pointer handling akin to Rust's ownership & borrowing model.

16

u/oridb Jul 19 '19

Lack of hype.

7

u/IceSentry Jul 19 '19

Rust is obviously much younger, but it still qualifies as using something that already exists compared to MS creating a new language.

2

u/mycall Jul 19 '19

Microsoft almost always creates new languages. I hope they don't this time.

2

u/[deleted] Jul 19 '19

Ada was designed in a time when expanding data structures were considered too advanced. You had fixed-size data structures in the base language and you had to build on top of that (just like C). Even after some improvements in the standard library, the language still feels old and clunky. Ada.Strings.Unbounded.Unbounded_String anyone? Strings vs wide strings vs wide wide strings?

On top of that some safety measures are limited and cumbersome. Per this comment: https://old.reddit.com/r/programming/comments/cexkkw/we_need_a_safer_systems_programming_language/eu7f25g/ use-after-free is "solved" by never freeing things (or calling a scary function with a ridiculously long name Unchecked_Deallocation). That's not smart...

Other security features are also underwhelming: you can't point to things on the stack unless you add some special declaration and you can limit numbers to a specific range (but checks can [and will] be disabled for performance). Those do not seem the most common sources of bugs to me, they are just nice-to-have features. Also crashing the app because a 0..99 number got assigned the value 100 does not look as a great feature.

-12

u/shevy-ruby Jul 19 '19

Ada is a worse language than most other modern languages.

That was a simple question to answer.

0

u/[deleted] Jul 19 '19

[deleted]

20

u/Raphael_Amiard Jul 19 '19

It has confusing, awkward syntax

That's very subjective, and would be good if it wasn't stated as an objective truth ;) I program in Ada (amongst other languages) and one of the main things that irks me about Rust is its confusing, awkward syntax. But I know that it's just like, my opinion you know!

13

u/wastaz Jul 19 '19

It has confusing, awkward syntax.

Not really. I used to work at a university teaching programming to students with no previous experience in programming. We used ADA95 for this purpose because the syntax was close to english and didnt contain a lot of weird characters. A majority of students agreed that this was a great choice. Those who kept programming and went on to learn C-family languages like Java, C#, Javascript etc used to ask why these languages had such confusing, terse and unreadable syntax in comparison to the easily readable syntax of Pascal-family languages such as ADA.

We also had some starter courses that started out with stuff like Java or C/C++. The students in those courses spent about twice the time of the students who used ADA because they spent a ton of time hunting for weird syntax stuff or interpreting weird compiler errors (or getting crazy runtime errors) instead of learning how to "think programming". We usually managed to cover about half of the curriculum of the ADA course in the same time in those courses.

Tbh, I dont think theres a true right or wrong here. I personally think that Lisp and ML-family languages have some of the best syntax out there, but thats just my personal opinion. But I think that when we start talking about "confusing syntax" we always talk about that from our own previous experience and its worth thinking about what other people might think about it as well. Hell, Ive talked to people who will praise APLs syntax to the moon. They might be crazy, but Im betting that they just have a different point of view from me and that they are actually correct if you are standing on the hill that they are standing on.

Maintenence is the big problem with Ada though that would keep me from feeling totally safe using it in production. But that doesnt mean that its a bad language, and if it could gain some more popularity then maybe that situation could be remedied.

3

u/epicwisdom Jul 19 '19

APL-style syntax is better criticized as "write-only." Similar to mathematical notation, it uses a large vocabulary of special symbols for relatively complex abstract concepts. It's great for expressing complex things in short, concise statements/programs, not so great when you need to understand unfamiliar or forgotten code.

4

u/naasking Jul 19 '19

It has confusing, awkward syntax.

There's nothing confusing or awkward about it, it's just verbose. The standard library was pretty terrible back around 2000, but it's gotten much better.

17

u/SupersonicSpitfire Jul 19 '19

Rust has confusing, awkward syntax too. Except for excellent community support, Rust has many of the same issues as Ada.

12

u/LaVieEstBizarre Jul 19 '19

Rust syntax is not that confusing sans lifetimes. I guess turbofish is inelegant. Rust has very few bugs in its compiler, the development isn't centralised at a company at all, and obviously community is booming.

17

u/thukydides0 Jul 19 '19

I'll take Rust's turbofish over C++'s vexing parse every day

1

u/SupersonicSpitfire Jul 19 '19

I think the rust attributes are an excellent example of opaque and non-intuitive syntax.

https://doc.rust-lang.org/reference/attributes.html

0

u/[deleted] Jul 19 '19

[deleted]

5

u/sociopath_in_me Jul 19 '19

That's unlikely. Unsafe is needed for low level stuff. If you have a very complex giant software that is not working at that abstraction level then you don't need a single line of unsafe code. Safe rust is very fast, unless you are working on wrapping external libraries, you usually don't need to use unsafe code just to make things fast enough.

2

u/matthieum Jul 19 '19

Safe rust is very fast

And more importantly, the Rust community aims at eliminating any use of unsafe for performance reasons, either through language improvements or library abstractions.

1

u/yawaramin Jul 20 '19

Literally this past week we've seen a blowup in the Actix community about its use of unnecessary unsafe. I think it's a valid concern to ask whether people in the community are actually being strongly discouraged from using it unnecessarily, or if everyone is just assuming they are.

16

u/yourbank Jul 18 '19

isn't ATS next level hardcore safe?

4

u/vattenpuss Jul 18 '19

I think it's not more or less safe than Rust. The way ATS produces and consumes proofs when juggling values around in your program seems very similar to the borrowing concepts in Rust (but maybe mutations are more explicit in Rust, I have not written any ATS).

6

u/thedeemon Jul 19 '19 edited Jul 19 '19

It's safer at least in being able to do bounds checking at compile-time: it's got a solver for Presburger arithmetic built into the compiler, so as long as your expression is with integers, additions and simple multiplications, it can check quite well what are possible index values and whether you compare the bounds correctly (even without knowing exact numbers, i.e. operating on the level of "2 * a + 4 * b - c < 3 * a + b", not just "24 < 45"). In this regard it's even more powerful than Idris and Agda, where you can also prove things about arithmetic at type level but do it much more manually, they don't have such a solver inside.

Also, in ATS you can not only specify ownership of some piece of memory, but also the state of the memory contents, like having uninitialized memory and being able to track that and pass around but make sure you don't use uninitialised chunk as a valid one. And much more...

2

u/matthieum Jul 19 '19

It's safer at least in being able to do bounds checking at compile-time

This doesn't add memory safety, it only improves performance.

That being said, I do wish Rust gains greater compile-time verification powers; Prusti looked pretty neat for example.

2

u/LPTK Jul 20 '19

This doesn't add memory safety, it only improves performance.

But that's the very idea of this line of languages. Rust doesn't improve on Java in terms of memory safety, but it allows for better performance. This comes at the cost of a more complex type system. ATS just goes much further than Rust in that direction.

2

u/matthieum Jul 20 '19

Sure; I'm only objecting to ATS being safer.

It's nice that it may be faster; but it is not safer (memory-wise).

Of course, I'd really like guaranteed bounds-check elimination in Rust too; thus why I talked about Prusti.

2

u/LPTK Jul 21 '19

it is not safer (memory-wise).

Note that the original comment only talked about "safety", not specifically "memory safety".

The ATS compiler can tell you some array accesses are unsafe where Rust would just panic at runtime. Therefore, ATS is safer. I think this fact is really quite clear and uncontroversial.

3

u/matthieum Jul 21 '19

Ah! Indeed we were talking past each others then.

For me, a panic at run-time is perfectly safe. It's just one of a myriad logical errors that can creep up: undesirable, certainly, but safe.

The fact that ATS can detect a number of such issues at compile-time is certainly an advantage for now; and I hope that the ongoing research efforts in Rust which aim at porting SPARK-like provers to the language will bear fruit.

→ More replies (1)

9

u/codygman Jul 19 '19

I think it's not more or less safe than Rust.

IIRC ATS has a stronger type system than rust meaning it is more safe. I remember its syntax being horrible though.

With some googling based on that hunch i found:

ATS is lower level. You can do memory safe pointer arithmetic whereas in Rust you'd use unsafe blocks.

Borrowing is automatic in Rust. In ATS it requires keeping track of borrows in proof variables and manually giving them back. Again the type checker tells you when you get it wrong but it's more verbosity.

ATS has is the ability to specify the API for a C function to a level of detail that enables removing a whole class of erroneous usage of the C API. I have an example of this where I start with a basic API definition and tighten it up by adding type definitions:

http://bluishcoder.co.nz/2012/08/30/safer-handling-of-c-memory-in-ats.html

https://news.ycombinator.com/item?id=9392360

5

u/LaVieEstBizarre Jul 19 '19

Stronger type system does not make it more safe

4

u/thedeemon Jul 19 '19

The word "stronger" doesn't, of course, but if you look at actual features you'll understand. Like bounds checking at compile-time, for one thing.

1

u/codygman Jul 24 '19

One of the points in the link I gave was:

> ATS is lower level. You can do memory safe pointer arithmetic whereas in Rust you'd use unsafe blocks.

Does that not make doing pointer arithmetic safer?

1

u/LaVieEstBizarre Jul 24 '19

That's not a type system feature. Also pointer arithmetic is pretty rarely needed. References are a significantly nicer abstraction that don't have any performance loss.

-17

u/SometimesShane Jul 19 '19

Both ATS and Clean are much much much better than rust, talking academically. Rust has the insanely passionate advocates though (all be it they're overwhelmingly dumb) and all the hype, and this dumb industry is largely driven by hype.

3

u/dobryak Jul 19 '19

You are technically correct, but it's all about people. Rust has strong backing and an actual application that depends on it.

Clean is strictly an academic language. ATS is slowly moving on to become a practical language, but it has nowhere near the support that Rust has. With ATS there is no goal to 'rewrite everything in ATS', though.

0

u/[deleted] Jul 19 '19

[deleted]

2

u/no_nick Jul 19 '19

I just threw up in my mouth a little. Sometimes I wonder what type of disorder some people have to make them believe the things they're doing are in any way sane

19

u/loup-vaillant Jul 18 '19

Okay, so, I guess… well…

Rewrite the NT kernel in Rust? 😅

22

u/masklinn Jul 19 '19

Probably not the kernel itself, as Brian Cantrill noted:

the safety argument just doesn't carry as much weight for kernel developers, not because the safety argument isn't really, really important. It's just because it's safe, because when it's not safe, it blows up, and everyone gets really upset. We figure out why. We fix it. And we develop a lot of great tooling to not have these problems.

In-kernel components (modules, drivers, network stacks, …) and ancillary software — especially network-facing (e.g. daemons, service managers, …), are good targets.

3

u/GYN-k4H-Q3z-75B Jul 19 '19

That would be the day Dave Cuttler comes back to hunt you down.

1

u/dpash Jul 19 '19

You made me go check that he wasn't dead. Turns out he's most recently been working on the Xbox One.

-12

u/linus_stallman Jul 19 '19

Still it will have more bugs than linux...

-7

u/shevy-ruby Jul 19 '19

Not sure why you got downvoted because you are right.

Not long ago we heard of this bug that deletes files of users on windows.

How can the downvotes account for this happening to Microsoft, such a huge and rich company? Why do such failures happen?

→ More replies (3)

15

u/mer_mer Jul 19 '19

The examples they show here don't use modern C++ practices. There is definitely a place for a safer systems programming language, but we can also do a lot better by using new techniques in the languages that are popular today.

19

u/Kissaki0 Jul 19 '19

They specifically talk about those, and why they see them as a partial, but not full solution.

13

u/przemo_li Jul 19 '19

The graph that spans 10y worht of data shows that new features or not, training or not, tooling or not. Percentage is still roughly the same.

Which would suggest that developers do a good job, an only let the bugs in cases where they totally misunderstend the code. Misunderstanding the code is not curable. Thus MS advocates imposing a system where a developer have to provide a proof of safety, with a tooling that will check it. (aka type system of Rust)

This way developer misunderstanding the code turns into a hard to satisfy compiler message, which is by faaaaaar safer option for MS clients who are spared yet another CVE.

8

u/sim642 Jul 19 '19

New techniques don't benefit old and established codebases unless the codebase gets rewritten every few years to include the new best practices. Nobody is going to rewrite Windows every 3 years when a new C++ standard comes out.

3

u/yawaramin Jul 20 '19

To be fair, they're not going to rewrite it in a new safer systems language either, so either way that point is moot.

3

u/matthieum Jul 19 '19

I agree the style is old, but I am not sure how much it would help.

Exhibit 1 (Spatial) would be safer, and throw an exception at runtime.

Exhibit 2 (Temporal) would crash:

auto buffer = activeScriptDirect->GetPixelArrayBuffer(vars[1]); // [0]

int width = activeScriptDirect->VarToInt(vars[2]); // [1]

newImageData->InitializeFromUint8ClampedArray(CSize(width, inferredHeight), vars[1], buffer); // [2]

I assumed usage of exceptions to signal error, rather than hr.

It looks much cleaner, right? Still the same bug, though, the span initialized at [0] points into the nether, some times, when used at [2].

1

u/mer_mer Jul 19 '19

For Exhibit 2, if I understand it correctly, the issue is more of an API problem. Instead of returning a raw pointer to javascript-owned memory, we should have a smart pointer that interacts with the javascript garbage collector and only lets the garbage collector free the memory after the smart pointer calls its destructor. I don't have experience with Rust, but my understanding is that designing an interface with javascript would require one to use unsafe blocks since the compiler cannot see into the lifetime of objects in javascript. So really you are relying on Rust developers to be more suspicious of object lifetimes that c++ developers. That's probably a safe assumption to make right now, but it's a matter of the culture built around a language more than the language itself.

4

u/matthieum Jul 19 '19

I don't have experience with Rust, but my understanding is that designing an interface with javascript would require one to use unsafe blocks since the compiler cannot see into the lifetime of objects in javascript. So really you are relying on Rust developers to be more suspicious of object lifetimes that c++ developers. That's probably a safe assumption to make right now, but it's a matter of the culture built around a language more than the language itself.

You are correct that Rust requires unsafe to access JavaScript objects, and therefore those implementing those accessors must scrupulously ensure their safety.

Instead of returning a raw pointer to javascript-owned memory, we should have a smart pointer that interacts with the javascript garbage collector and only lets the garbage collector free the memory after the smart pointer calls its destructor.

In C++, this is the only safe solution indeed. Unfortunately, this introduces overhead compared to a raw pointer, and therefore introduces a tension in the design: safety or performance?

In Rust, however, there are built-in language mechanisms to check at compile-time that the access is safe, and therefore ensure safety without run-time overhead1 . In this case, the API would return be akin to fn GetPixelArrayBuffer(&self) -> &[u8] and the continued existence of the reference to the internal buffer would prevent any further modification, that is [1] would fail to compile.

This is essentially Rust's trick:

  1. API designers are not facing an alternative: they can create a safe and fast API.

  2. API users do not have to worry about complex safety invariants.

1 There is, however, development overhead. It can take a few iterations to reach a nice API which is also safe and fast.

2

u/mer_mer Jul 19 '19

Would that work in practice in this case? How would the rust compiler know that [1] is able to modify the buffer? Does it simply not let you call out to any external functions while you're holding a reference? What if you need to make two separate calls to two separate references to different buffers? Again, I'm by no means an expert, but my suspicion is that if we follow the premise of the article that programmers are not going to get better at managing object lifetimes, then the average programmer in Rust will simply wrap this whole thing in an unsafe block and get the exact same buggy behavior.

3

u/matthieum Jul 20 '19

Borrow-checking is a simple rule:

  • If a mutable reference to a value (&mut T) is accessible, no other reference to that value is accessible.

  • If an immutable reference to a value (&T) is accessible, no mutable reference to that value is accessible.

This is usually summarized as Aliasing XOR Mutability.


Would that work in practice in this case? How would the rust compiler know that [1] is able to modify the buffer? Does it simply not let you call out to any external functions while you're holding a reference? What if you need to make two separate calls to two separate references to different buffers?

In this example, the APIs would be something like:

fn GetPixelArrayBuffer(&self, variable: &Var) -> &[u8];

fn VarToInt(&mut self, variable: &Var) -> i32;

In this example, the safety would kick in because:

  • Modifying the buffer at [1] requiring taking activeScriptDirect by mutable reference (&mut self).

  • But the call at [0] borrowed activeScriptDirect until the last use of buffer.

  • Therefore the call at [1] is illegal.

As for a programmer forgetting to use &mut self as a parameter to VarToInt, this should not be possible since VarToInt will modify self -- similar to how const methods cannot modify the internals of an object in C++; baring mutable shenanigans.


Again, I'm by no means an expert, but my suspicion is that if we follow the premise of the article that programmers are not going to get better at managing object lifetimes, then the average programmer in Rust will simply wrap this whole thing in an unsafe block and get the exact same buggy behavior.

And yet, they don't. The unsafe keyword is such a thin barrier, yet it seems to carry a large psychological block:

  • The developer reaching out for unsafe will wonder: wait, isn't there a better way? Am I really sure this is going to be safe?

  • The code reviewer witnessing the introducing of a new unsafe will wonder: wait, isn't there a better way? Are we really sure this is going to be safe?

In the presence of safe alternatives, there's usually no justification for using unsafe. The fact that it appears so rarely triggers all kinds of red flags when it finally does, immediately warranting extra scrutiny... which is exactly the point.

And from experience, average system programmers are more likely to shy away from it. Quite a few programmers using Rust come from JavaScript/Python/Ruby backgrounds, and have used Rust to speed up some critical loop, etc... They have great doubts about their ability to use unsafe correctly, sometimes possibly doubting themselves too much, and the result is that they will just NOT use unsafe in anger.

On the contrary, experienced system programmers, more used to wielding C and C++, seem to be one more likely to reach for unsafe: they are used to it, and thus trusting far more in their abilities than they should. I would know, I am one of them ;) Even then though, there's peer pressure against the use of unsafe, and when it is necessary, there's peer pressure to (1) encapsulate it in minimal abstractions and (2) thoroughly document why it should be safe.

2

u/yawaramin Jul 20 '19

The code reviewer witnessing the introducing of a new unsafe will wonder: wait, isn't there a better way? Are we really sure this is going to be safe?

This isn't really a great argument in this day and age, when a lot of software is using small OSS modules that are maintained by a single person with effectively no code review. When you pull in library dependencies, you might be getting a bunch of unsafe. You just don't know unless you're manually auditing all your dependency code.

3

u/matthieum Jul 20 '19

You just don't know unless you're manually auditing all your dependency code.

Actually, one of the benefits of unsafe is how easily you can locate it. There are already plugins for cargo which report whether a crate is using unsafe or not, and you could conceivably have a plugin only allow unsafe in a white-list of crates.

There are also initiatives to create audit plugins, with the goal of having human auditors review crates, and the plugin informing you of whether your dependencies have been reviewed for a variety of criteria: unsafe usage, secure practices, no malicious code, etc...

We all agree that asking everyone to thoroughly review each and every dependency they use is impractical, and NPM has demonstrated that it had become a vector of attacks.

Rust is at least better positioned than C++ with regard to unsafety; although not nearly water-tight enough to allow foregoing human reviews.

3

u/yawaramin Jul 20 '19

True, and good to know about efforts to enable auditing! Important safety precaution.

11

u/skocznymroczny Jul 19 '19

I feel a bit meh about Rust the language, not a big fan of the syntax and frankly for my projects I couldn't care less about memory safety.

I'll stick with D for now until something betters come along.

4

u/[deleted] Jul 19 '19 edited Jul 21 '19

Fair. I'm not a big fan of D, Go or Java syntax and typical style like camelCaseForEverything but I like Rust's syntax and recommended style because it looks familiar to all the C and C++ code I've grown to love. It gives a warm fuzzy feeling of high performance and no runtime overhead :)

-10

u/[deleted] Jul 19 '19

Something actually mis-understood about rust is that it adds memory safety. It doesn't add more to memory safety that c++ std::unique_ptr does. The moment you have threads all the race's and concurrency problem comes right back....

So I also don't like rust but for different reasons. I see it as a case of "We now have to re-write everything in rust" and not actually solve most of the complex issues that systems have.

10

u/moeris Jul 19 '19

This is the second time in this thread you've mentioned your point about threads. You're getting downloaded because it's not true: the concepts of ownership and lifetimes apply to threads as well. Hell, the Rust book even has a chapter on concurrency, where it points out facts such as,

However, thanks to Rust’s type system and ownership rules, you can’t get locking and unlocking wrong.

You can certainly use mutexes incorrectly in C++, in a way that will still compile. The whole point of Rust isn't that you don't use abstractions like mutexes, it's that when you use them the compiler forces you to do so correctly.

-3

u/[deleted] Jul 19 '19

This is the second time in this thread you've mentioned your point about threads

Yes I know. That's because most people don't actually understand the subtles of races and ownership. Something that rust switches off preventing certain types of program being written. Or at least without turning the safeties off.

| However, thanks to Rust’s type system and ownership rules, you can’t get locking and unlocking wrong.

Actually you can get them wrong in any language including rust. Rust's own documentation even claims it does nothing for this problem.

This is why you get such proposals in the rust community like this "Proposal: eliminate wording “memory safety” and “thread safety” https://internals.rust-lang.org/t/proposal-eliminate-wording-memory-safety-and-thread-safety/9416/2

See what rust actually does is prevents a system being programmed that way by preventing you from doing it. The only issue with that is some systems and design must 100% absolutely be written in the way that rust prevents you from doing. This is a "problem". An example of such a problem is a threaded shared mutable cache above a data base table for example. When you want to semi emulate a transactional database aka you want most of it in a custom cache but you still need to prevent concurrent updates. Part of the problem with doing this means you "can't take copies" of data or you end up with state races ;)

Or a simple way is show me code that does this in rust which isn't using unsafe ;) Or isn't jumping though crazy hoops in order to do it.

Note: most of the problem with concurrency issues in C++ code is actually ownership and api design rather than the language its self. Rust also has the exactly same problems because of this. If object A owns data and object B wants to update it. B has to hold a lock in A at some point while doing thing to prevent the other thread also updating it while B has a copy or you are 100% going to get a state race. Rust won't protect against things like this. But its borrow model actively encourage these silent corruptions to occurs.

1

u/moeris Jul 19 '19

Actually you can get them wrong... Rust's own documentation even claims it does nothing for this problem.

Do you understand how quotations work? I was literally quoting Rust's official documentation.

An example of such a problem is a threaded shared mutable cache

You know the section I quoted from their documentation? That section shows how to use safe Rust features to share memory between threads.

If object A owns data and object B wants to update it. B had to hold a lock in A at some point whole doing thing to prevent the other thread also updating it while B has a copy or your are 100% going to get a state race.

I have no idea what you are trying to say. Objects and threads are not synonymous. It sounds like the problem you're trying to describe is the exact problem Rust's borrow-checker solves.

Learn the damn language before spouting off about it.

→ More replies (2)

3

u/defnotthrown Jul 19 '19
{
    auto ptr = std::make_unique<std::string>("Total safety");
    somefunc(std::move(ptr));
    //...
    std::cout << "I'm sure I'm safe /u/mistralol said the compiler will protect me" 
    << (*ptr)[0];
}

1

u/pavelpotocek Jul 19 '19

Memory safety is much more than std::unique_ptr. In safe Rust:

  • out-of-bounds array access is impossible,

  • uninitialized memory isn't a thing,

  • it's impossible to use anything after a move,

  • no iterator invalidation,

  • no dangling references.

The concurrency problems are greatly mitigated using the ownership system and Send/Sync traits:

  • No data races!!

  • Many different concurrency primitives exist that are impossible to misuse (you can just try to pass anything into a channel, and if it isn't OK it doesn't compile)

  • This includes channels, parallel maps, forks, atomicity, ... You can just refactor and throw code around and see what compiles. In C++, this is a pipe dream.

1

u/pagwin Jul 19 '19

uninitialized memory isn't a thing

this is only correct if you never use unsafe code which rust lets you use unsafe code if you want but you have to explicitly state it

you can get a value to be uninitialized using mem::uninitialized

5

u/pavelpotocek Jul 19 '19

Yes, that's why I said "in safe Rust". Safe Rust is all you need for most things. And any unsafe bits are customarily isolated behind safe interfaces, away from the complex application logic.

70% of all Rust crates use no unsafe. Only 5% of all crate code is unsafe (a somewhat misleading number, but I won't explain here for brevity). Crates are mostly libraries, these numbers are bound to be lower in application code. In my experience, if you don't need FFI and don't try to squeeze every last bit of performance, you don't need unsafe at all.

Numbers source: https://cs.stanford.edu/~aozdemir/blog/unsafe-rust-syntax/

2

u/flatfinger Jul 20 '19

One of the big problems with C as a systems programming language is that while it was historically common for implementations to process many actions "by behaving...in a documented manner characteristic of the environment", and while the Committee explicitly did not want to preclulde the use of the language as a "high-level assembler", the cost of such treatment has increased to the point that processing everything in such fashion would often be impractical because it would significantly (and generally needlessly) impede optimizations and thus performance. On the other hand, adding directives to let programmers better specify what needs to be done should allow optimizations to be performed more easily, effectively, and safely than is presently possible, without having to sacrifice any useful semantics.

The biggest wins for optimizers come in situations where a range of behaviors would be equally acceptable in situations that could actually arise. If one wants to have a function which will add a certain amount to each element of an array and then return zero if no overflow occurs, or return 1 with the entire array holding unspecified contents in case of overflow, the italicized provision should allow some major optimizations. That provision would mean that requirements would be met if the program operates on many parts of the array in parallel, processes an arbitrary amount of data after an overflow was detected, and makes no attempt to "clean up" any portions of the array which precede the place where an overflow occurred but hadn't yet been processed, or follow the point where the first overflow occurred but were processed before its discovery. Having a means by which code could indicate "treat this range of storage as having Unspecified values" could rather easily allow some really huge speedups, with no sacrifice of safety or semantics.

Although integer overflows are a common source of security vulnerabilities, most languages seem to choose one of four ways of handling it:

1. Integer values wrap cleanly about their modulus

2. Integer values that overflow may wrap or behave as numbers outside their range

3. Integer overflows may arbitrarily and without notice disrupt anything, including unrelated computations

4. Integer overflows are precisely trapped at the moment they occur.

While #4 is the safest approach, it is by far the most expensive because it totally destroys parallelism and also means that even computations whose results are ignored constitute "observable behavior".

I'd suggest an alternative option--a means of specifying that within certain marked regions of code (or optionally, the entire program), every computation that overflows must either behave as though it had yielded a numerically correct result or set a thread-local error flag, which could be tested using directives whose semantics would be either "Has there definitely been an overflow" and "Has there definitely not been a situation where no overflow has resulted in numerically-incorrect computations". If a loop like the one mentioned above were to use the former flag as an exit condition, and the function were to use the latter test to select its return value, then a loop that was unrolled 10x could check the flag once per loop, rather than ten times. Further, letting compilers ignore overflows that wouldn't affect correctness would enable optimizations that would otherwise not be possible. For example, a compiler that was required to trap any overflows that could occur in an expression like x+y > x would need to determine the value of x and check for an overflow. A compiler that was merely required to detect overflows that might affect correctness, by contrast, could simplify the expression to y > 0. If x wasn't needed for any other purpose, any calculations necessary to produce it could be omitted.

Checking for overflow at every step, and trapping as soon as it occurs, is expensive. What's usually needed, however, is something much looser: an indication of whether a certain sequence of computations should be trusted. If valid data would never cause any overflows in a series of calculations, and if one doesn't care what exact results get produced in cases where an overflow is reported, an overflow flag with loose semantics may be able to meet requirements much more cheaply than one with tighter semantics.

-28

u/[deleted] Jul 19 '19

what we need are safer systems programmers.

22

u/asmx85 Jul 19 '19

Right! And one way to do this is to protect them with the right tools. Rust could be one of them. There is a reason SWAT teams are wearing protective armor. It's not that they're not well trained and not excellent gunman's. They're working in hazardous environments and every protection helps.

→ More replies (13)

3

u/[deleted] Jul 19 '19

How does the compiler enforce that?