Hacker Newsnew | past | comments | ask | show | jobs | submit | more Measter's commentslogin

For Rust vs C++, I'd say it'll be much easier to have a complete understanding of Rust. C++ is an immensely complex language, with a lot of feature interactions.

C# is actually fairly complex. I'm not sure if it's quite at the same level as Rust, but I wouldn't say it's that far behind in difficulty for complete understanding.


So in Rust an unsafe block and an unsafe function mean two different things. An unsafe block allows you to do things that are unsafe, such as dereference raw pointers, access union fields, calling unsafe functions, etc.

Unsafe functions mark that the caller is responsible for upholding the invariants necessary to avoid UB. In the 2021 and earlier editions, they also implicitly created an unsafe block in the body, but don't in 2024.

Or, in a more pithy tone: an unsafe block is the "hold my beer" block, while an unsafe function is a "put down your beer" function.


A segfault is not the program performing a runtime check and doing a controlled shutdown. A segfault is the OS detecting the program doing something it's not allowed to and killing it.


Only if that memory page is unmapped, and only if the optimizer doesn't detect that it's a null pointer and start deleting verification code because derefing null is UB, and UB is assumed to never happen.


How common is this in practice?


Compilers regularly delete null pointer checks when they can see that the pointer is dereferenced.


(GCC controls this with `-fno-delete-null-pointer-checks` https://gcc.gnu.org/onlinedocs/gcc/Optimize-Options.html#ind... )


> Take Herb Sutter for example, who argues that "memory safety" as defined in this article is an extreme goal and we should instead focus on a more achievable 95% safety instead to spend the remaining effort on other types of safety.

One thing I've noticed when people make these arguments is that they tend to ignore the fact that most (all?) of these other safeties they're talking about depend on being able to reason about the behaviour of the program. But when you violate memory safety a common outcome is undefined behaviour, which has unpredictable effects on program behaviour.

These other safeties have a hard dependency on memory safety. If you don't have memory safety, you cannot guarantee these other safeties because you can no longer reason about the behaviour of the program.


Herb Sutter's article on this.[1]

For C/C++, memory safety is a retrofit to a language never designed for it. Many people, including me, have tried to improve the safety of C/C++ without breaking existing code. It's a painful thing to attempt. It doesn't seem to be possible to do it perfectly. Sutter is taking yet another crack at that problem, hoping to save C/C++ from becoming obsolete, or at least disfavored. Read his own words to see where he's coming from and where he is trying to go.

Any new language should be memory safe. Most of them since Java have been.

The trouble with thinking about this in terms of "95% safe" is that attackers are not random. They can aim at the 5%.

[1] https://herbsutter.com/2024/03/11/safety-in-context/


> Most of them since Java have been

The most popular ones have not been necessarily. Notably Go, Zig, and Swift are not fully memory safe (I’ve heard this may have changed recently for swift).


Could you expand on how Go isn’t memory safe?


Go's memory safety blows up under concurrency. Non-trivial data races are Undefined Behaviour in Go, violating all safety considertions including memory safety.


While that keeps being given as example, Go is not C, C++, Objective-C levels of memory corruption opportunities.

Lets not let the perfect be the enemy from good.

Even with all my criticism of many Go's design decisions, I rather have more infrastructure code being written in Go than C, and derived languages.

Or any of the supposed better C alternatives, with manual memory management and use after free.


Go maps don't have enough locking to be thread safe, as I understand it. That was true at one time. Was it fixed?


I would not expect that it makes sense to provide this as the default for Go's hash table type, my understanding is that modern Go has at least a best effort "fail immediately" detector for this particular case, so when you've screwed this up your code will exit, reporting the bug, in production and I guess you can curse "stupid" Go for not allowing you to write nonsense if you like, or you could use the right tool for the job.


Isn't it strange that they created a new language where one of its main selling points is concurrency, and punted on that design issue.

(Yes I'm aware that Go literature 'encourages' the use of Channels and certain patterns)


Similar to how Rust clearly is not memory safe, Go is also not memory safe.


Not sure what this is saying but you can create a trivial concurrent program that violates the memory safety guarantees Go is supposed to provide [1]: https://biggo.com/news/202507251923_Go_Memory_Safety_Debate

That is not true of Rust.


> [You can] create a trivial concurrent [Go] program that violates the memory safety guarantees ... > That is not true of Rust.

It's not supposed to be, but Rust does have plenty of outstanding soundness bugs: https://github.com/rust-lang/rust/issues?q=state%3Aopen%20la...

Rust as intended is safe and sound unless you write u-n-s-a-f-e, but as implemented has a ways to go.


There are also limits to what the borrow checker is capable of verifying. There will always be programs which are valid under the rules the borrow checker is enforcing, but the borrow checker rejects.

It's kinda annoying when you run into those. I think I've also ran into a situation where the borrow checker itself wasn't the issue, but rather the way references were created in a pattern match causing the borrow checker to reject the program. That was also annoying.


Polonius hopefully arrives next year and reduces the burden here further. Partial field borrows would be huge so that something like obj.set_bar(obj.foo()) would work.


Given the troubles with shipping Polonius, I imagine that there isn't much more room for improvements in "pure borrow checking" after Polonius, though more precise ways to borrow should improve ergonomics a lot more. You mentioned borrowing just the field; I think self-referential borrows are another.


That support could be Microsoft driven. Parts of Windows 11 are written in Rust, and having that platform in Tier 1 makes their lives easier.


There's no push to add Debian's officially supported platforms to Rust because Rust already supports those platforms.


Based on this, and many other similar threads, it's the anti-Rust zealots insulting Rust users.


Zig only does bounds checking by default in Debug and ReleaseSafe builds. If you build with ReleaseFast or ReleaseSmall it will happily do an out of bounds read: https://godbolt.org/z/733PxPEPY


That's a matter of how policy is set. You can set it to on or off for a particular function, too. The point is that language offers sound spatial safety just as much as Rust does (and both allow you to turn it on or off in particular pieces of code).


Defaults and ecosystem approach matter a lot, though.

The whole Rust ecosystem is heavily biased towards prioritising memory safety and "safe by construction" .

This is evident in the standard library, in how crates approach API design, what the compilation defaults are, ...

In 6+ years of using Rust the only time I had to deal with segfaults was when working on low level wrappers around C code or JIT compilation.

Zig has some very interesting features, but the way they approach language and API design leaves a lot of surface area that makes mistakes easy.


Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: