Preferences

And do you say that C offers these guarantees ?

Real world C software does not read like software written by people who are in fact upholding those guarantees you say C could equally have. It reads as though they think such a guarantee is a joke or an irrelevance. It's not rare for me to run into people who think C's pointers are just indexing into a massive array of all RAM (or its equivalent on today's systems with virtual addressing), that's not just not in the same ballpark as a safe C program, that's playing a different sport on another continent.


You seem to be suggesting that a language being safe or unsafe is a social contract rather than a technical property of the language.

>And do you say that C offers these guarantees ?

No, that would be silly, and it's an illustration of why it is silly to say that a language guarantees X if it is the programmer who must check that X holds. If we go down that route (which, to repeat, would be silly), then we can make C safe without any technical changes just by adding some language to the standard saying that C programmers are obliged to ensure that their code maintains a certain list of invariants. When you say that "Rust makes the same guarantees regardless of the unsafe keyword", it seems to me that you are doing something equally pointless.

> You seem to be suggesting that a language being safe or unsafe is a social contract rather than a technical property of the language.

Quite some way up this thread pizlonator insists that each programming language defines memory safety differently, quantifying some as "weaker" or "stronger" and giving the example that Rust has the `unsafe` keyword and so that's weaker than Fil-C.

That's what we were discussing when you jumped in with your C hypothetical.

You apparently instead believe in a single universal "safety" and every language is either absolutely safe or unsafe according to foldr for whatever that's worth - but that's not what we were talking about.

No, I just think that Rust is less safe than it would be if it didn’t have the unsafe escape hatch.

I think you’re taking issue with how pizlonator phrased his post rather than addressing the substance of his point that Fil-C does not have the ‘unsafe’ escape hatch and is therefore safer in this respect. Sure, Rust uses a pretty standard definition of memory safety when talking about the desired property of the program, but pizlonator is talking about the definition of memory safety that the Rust compiler actually guarantees that Rust code will meet, which (when you include unsafe-marked code) is a conditional and weaker one.

I still can't agree with weaker. Yes, it's conditional on the unsafe code actually obeying the rules, and on the tooling, but Fil-C has the same situation, there will be bugs in the compiler, indeed in some cases the same bugs because LLVM has plenty of bugs.

Crucially unsafe Rust doesn't have weaker rules, it has the same rules, that's my whole thrust here. The change is that the burden of obeying those rules is on you, the programmer, and that's a heavy burden. But it is very much possible for skilled practitioners to carefully achieve that. It's very similar skill to writing C++ stdlib implementations. Aria's "Pre-pooping your pants" essay is colourful but ultimately it's the same sort of thing strong exception guarantees are made of in C++. We go in eyes open, expecting the worst so that we're pleasantly surprised when it doesn't happen.

It's not practical for humans to write code like this all day, everyday, they make too many mistakes that's the problem in C or C++ - but, seems like it is practical for some skilled people, sometimes, with the benefit of oversight from similarly skilled peers.

If that isn't enough for you I have good news and I have bad news. The bad news is that for general purpose software too bad, we've known since the middle of last century that we can't do better than this. Fil-C isn't magic, nor are Java and C#. At runtime we detect we can no longer assure correct operation and we abort, this might not be OK, but we can't do better.

The good news is that we can do better if we're willing to sacrifice generality. The difficulties all come from the fact that ultimately if we're a general purpose programming language we can be made into a Gödel number and then obliged to perform computations on ourselves and we're toast. But we can invent useful languages which aren't powerful enough to do that. Want to decompress ZIP files? No generality needed. Crop some JPEGs? Not needed. Validate whether these graphs are isomorphic? Ditto.

Rust is a general purpose language, but you might well not need one for your problem, and I say we should prefer not to use a general purpose language when we don't need one.

I don't think it makes sense to compare non-invariant-respecting unsafe blocks to compiler bugs. It would make sense to do so if unsafe blocks were only present in a highly-verified Rust stdlib, but we both know that's not the case.

>Crucially unsafe Rust doesn't have weaker rules, it has the same rules, that's my whole thrust here. The change is that the burden of obeying those rules is on you, the programmer, and that's a heavy burden.

Now we're circling back to my argument about C. The C standards committee could declare that 'unsafe C' (i.e. all C) has these very same rules (which C programmers have the heavy burden of obeying). Would this instantly convert C into a memory safe language? Of course not! It's an empty semantic gesture. Similarly, merely saying "Rust programmers are obliged to respect the following invariants inside unsafe blocks!" does nothing to actually decrease the risks associated with unsafe blocks (leaving aside whatever exhortive success such admonitions might have).

What next, if we accept this logic? Is Perl a language with strict static typing, but "the burden of checking the types falls on you, the programmer"?.

> ... then we can make C safe without any technical changes just by adding some language to the standard saying that C programmers are obliged to ensure that their code maintains a certain list of invariants.

In Rust you can use #![forbid(unsafe_code)] to totally forbid unsafe code in your codebase. Rust also checks for memory safety at compile time, these are strong guarantees that ensure that if the code compiles it is memory safe.

I'm aware of that, but I'm responding to the original claim that "Rust makes the same guarantees regardless of the unsafe keyword" (see https://www.hackerneue.com/item?id=46262774)
Ah. I agree with you. When unsafe is used the borrow checker cannot check for memory safety, the programmer has to provide the guarantees by making sure their code does not violate memory safety, similar to programming in C.

But unsafe Rust is still far better than C because the unsafe keyword is visible and one can grep it and audit the unsafe parts. Idiomatic Rust also requires that the programmer provides comments as to why that part is unsafe.

I think making things more explicit with "unsafe" is an advantage of Rust, but I think "far better" is a bit of an exaggeration. In C you need to audit pointer arithmetic, malloc/free, casts and unons. If you limit pointer arithmetic to a few safe accessor functions and have a documented lifetime rules, this is also relatively simple to do (more difficult than "grep" but not much). Vice versa, if you use a lot of "unsafe" in Rust or in complicated ways, it can also easily become possible to guarantee safety. In contrast to what people seem to believe, the bug does not need to be inside in unsafe block (a logic error outside can cause the UB inside unsafe or a violation of some of Rust's invariants inside unsafe can allow UB outside of unsafe) and can result even from the interaction of unsafe blocks.

The practical memory safety we see in Rust is much more the result of trying hard to avoid memory safety issues and requiring comments for unsafe blocks is part of this culture.

> the bug does not need to be inside in unsafe block

The argument is that while you wouldn't in fact fix the bug by modifying the unsafe code block, the unsafe code block was wrong until you fixed the other code.

For example imagine if a hypothetical typo existed inside RawVec (the implementation details of Vec) causing the growable array to initially believe it has 1 element inside it, not 0 even though no space has been allocated and nothing was stored. That's safe code, and of course the correct fix would be to change it from 1 to 0, easy. But this broken type is arguably broken because the unsafe code would deference a pointer that isn't valid, trying to reach that non-existent value. It would be insane, perhaps even impossible, to modify that code to somehow handle the "We wrote 1 instead of 0" mistake, when you could instead fix the bug - but that is where the theoretical fault lies.

This item has no comments currently.

Keyboard Shortcuts

Story Lists

j
Next story
k
Previous story
Shift+j
Last story
Shift+k
First story
o Enter
Go to story URL
c
Go to comments
u
Go to author

Navigation

Shift+t
Go to top stories
Shift+n
Go to new stories
Shift+b
Go to best stories
Shift+a
Go to Ask HN
Shift+s
Go to Show HN

Miscellaneous

?
Show this modal