Live data from Hacker News

The Problem with Friendly C

blog.regehr.org

161–170 of 174 posts

Re: The Problem with Friendly C

#161
post #132

Earlier quoted context omitted.

The problem, as I understand it, is that Rust's ownership semantics don't accommodate for the kinds of programming needed by 0 dynamic allocations. Lots of intrusive nodes pointing freely at one another, is an example. I've heard some Rust libraries approximate intrusive allocations almost perfectly, but not quite perfect.

As long as you're willing to use unsafe, you can write things exactly as you can as in C. That's of course, not as satisfying as not needing to, but by encapsulating that in a library and providing a safe interface, downstream users still get the benefits.

Can a safe interface always be provided?

Re: The Problem with Friendly C

#162
post #132

Earlier quoted context omitted.

The problem, as I understand it, is that Rust's ownership semantics don't accommodate for the kinds of programming needed by 0 dynamic allocations. Lots of intrusive nodes pointing freely at one another, is an example. I've heard some Rust libraries approximate intrusive allocations almost perfectly, but not quite perfect.

Rust's ownership and borrowing rules are entirely a compile-time thing: they just determine what objects can be safely used, and when. There is absolutely no need to use dynamic allocation to benefit from Rust's ownership and borrowing checks. And none of the other core language facilities (primitive types, unsafe pointers, fixed-size arrays) require dynamic allocation either. What does require dynamic allocation is…

As far as I know, nobody managed to create a comprehensive intrusive data structure library that behaves as optimally as in C, due to the ownership semantic restrictions.

Re: The Problem with Friendly C

#163

Earlier quoted context omitted.

It's not really about optimisations, but more about being able to translate directly to fast machine instructions (which may behave differently on different machines). What I'm talking about is not adding checks, but what happens when the compiler knows that something has undefined behavior through e.g. Constant propagation. This happens in the optimizer on IR level. The undefined behaviour is there to make translati…

> What I'm talking about is not adding checks, but what happens when the compiler knows that something has undefined behavior through e.g. Constant propagation. But ... either the compiler can prove at compile time that the program has undefined behaviour, in which case it should simply refuse to compile, or it can not, in which case it has to emit a runtime check to determine when undefined behaviour "is about to ha…

> But ... either the compiler can prove at compile time that the program has undefined behaviour, in which case it should simply refuse to compile, or it can not, in which case it has to emit a runtime check to determine when undefined behaviour "is about to happen", which is potentially expensive?

It's more fine-grained: The front end (namely clang in llvm) in easy cases can already see that something is always undefined behaviour and it will omit a warning (for instance through constant propagation). This happens with `i = 32; xThe optimizer then does stuff like inlining function that lets it see even more places where there sure will be undefined behaviour. This is the point I'm talking about. The compiler already knows that it will be undefined behaviour - no checks are needed. Performance is of no concern here, as the program already is in a buggy state and may just quit. This will happen when you have the function call f(32) in one function and in `f` there's a shift `x Where performance matters is the case where the compiler doesn't know the parameter. To be completely safe the compiler would need to check wether the shift amount is over 31 (`if shamt > 31 { exit(0)} else { x >> shamt }`). Here there's also a performance hit in case there is no undefined behaviour occurring.

Re: The Problem with Friendly C

#164
post #162

Earlier quoted context omitted.

Rust's ownership and borrowing rules are entirely a compile-time thing: they just determine what objects can be safely used, and when. There is absolutely no need to use dynamic allocation to benefit from Rust's ownership and borrowing checks. And none of the other core language facilities (primitive types, unsafe pointers, fixed-size arrays) require dynamic allocation either. What does require dynamic allocation is…

As far as I know, nobody managed to create a comprehensive intrusive data structure library that behaves as optimally as in C, due to the ownership semantic restrictions.

So just bypass the ownership checks, with `unsafe`. It gives up on memory safety, but you still keep all the other benefits of a modern language: algebraic data types, pattern matching exhaustiveness checks, parametric polymorphism, etc. So, even when writing `unsafe` code, Rust is strictly superior to C and C++.

Also, I would contend that, for the vast majority of programs, the benefits of a simple, hierarchical, statically enforceable resource management scheme like RAII outweigh the downsides. Programs that need more sophisticated approaches are a very small minority.

Re: The Problem with Friendly C

#165

Earlier quoted context omitted.

> What I'm talking about is not adding checks, but what happens when the compiler knows that something has undefined behavior through e.g. Constant propagation. But ... either the compiler can prove at compile time that the program has undefined behaviour, in which case it should simply refuse to compile, or it can not, in which case it has to emit a runtime check to determine when undefined behaviour "is about to ha…

> But ... either the compiler can prove at compile time that the program has undefined behaviour, in which case it should simply refuse to compile, or it can not, in which case it has to emit a runtime check to determine when undefined behaviour "is about to happen", which is potentially expensive? It's more fine-grained: The front end (namely clang in llvm) in easy cases can already see that something is always unde…

> The compiler already knows that it will be undefined behaviour - no checks are needed. Performance is of no concern here, as the program already is in a buggy state and may just quit.

You have it all backwards. If there is some place in the control flow of the program where the compiler can prove that the program is guaranteed to run into undefined behaviour, then it's either because it has proved that the whole program unavoidably runs into undefined behaviour (in which case the compiler should simply refuse to compile), or because there is a branching instruction preceding that part of the code that divides the control flow into the part that the compiler has proved to run into undefined behaviour and the part where the compiler has not proved that, in which case you obviously have a branching instruction (and probably associated condition computations) that has the function of "performing a runtime check" and that introduces an overhead vs. simply dropping that check/branch from the code.

If the program somehow determines at runtime whether it is in a defined or an undefined state, there has to be code to make that determination--code that the optimizer could avoid producing/eliminate as far as language semantics are concerned, as it doesn't have to guarantee any specific behaviour in the undefined case, so simply having the undefined case execute the same code as the defined case, thus avoiding the check to discern the cases, makes the defined case faster.

Re: The Problem with Friendly C

#166
post #161

Earlier quoted context omitted.

As long as you're willing to use unsafe, you can write things exactly as you can as in C. That's of course, not as satisfying as not needing to, but by encapsulating that in a library and providing a safe interface, downstream users still get the benefits.

Can a safe interface always be provided?

I can't say that I'm able to speak to every single possible program ever, but at _some_ level, it should end up safe, yeah.

Re: The Problem with Friendly C

#167
post #142

Earlier quoted context omitted.

That doesn't do IPC though right? When I say IPC, I mean where you have a Rust process and and a C/C++ process running with different privileges. When people say FFI, they mean Rust code and C code running in the same process. I thought Chromium had a library for this (for multiple C++ processes), but I guess it is sort of ad hoc now? https://www.chromium.org/developers/design-documents/inter-p... https://www.chromiu…

https://github.com/pcwalton/gaol

This seems like a great start, but it would be far more useful if there was a C/C++ side. I only see Rust examples, which makes me think it is for Rust Rust communication.

Though it's great to see this, because just rewriting in Rust -- while fantastically expensive -- isn't necessarily enough. You need multiple security measures. The grandparent comment was mistaken about this.

Re: The Problem with Friendly C

#168

Earlier quoted context omitted.

> As I see it that's a variant of PADDW, so whatever PADDW does is what matters, and PADDW does twos complement. Nope, PADDW is a variant of PADDSW, so whatever PADDSW does is what matters, and PADDSW does saturating addition. Or in other words: You are defining "normal" to mean "twos-complement with wrap-around" after all, which makes it rather unsurprising that every "normal" instruction according to your definitio…

"ascii adjust after addition" > Nope, PADDW is a variant of PADDSW, so whatever PADDSW does is what matters, and PADDSW does saturating addition. That's like arguing that Red Sports Car is a variant of Red Sports Car with Stripes. The one that adds adjectives is the variant, the one without those adjectives is the base. If PADDW did saturating arithmetic and PADDWW did wrapping, I would agree with you, but that's not…

It is, however, what many DSPs do.

Re: The Problem with Friendly C

#169
post #129

Earlier quoted context omitted.

I haven't used it yet, but apparently Rust's FFI with C (in both directions) is quite good: https://doc.rust-lang.org/book/ffi.html

In fact, we removed language features in order to gain a zero-cost interop.

Which ones?

Re: The Problem with Friendly C

#170
post #168

Earlier quoted context omitted.

"ascii adjust after addition" > Nope, PADDW is a variant of PADDSW, so whatever PADDSW does is what matters, and PADDSW does saturating addition. That's like arguing that Red Sports Car is a variant of Red Sports Car with Stripes. The one that adds adjectives is the variant, the one without those adjectives is the base. If PADDW did saturating arithmetic and PADDWW did wrapping, I would agree with you, but that's not…

It is, however, what many DSPs do.

Many DSPs don't support byte addressing or arithmetic on 8-bit integers which a huge amount of non-DSP C code relies on. They're not exactly friendly to code not written specifically to run on the DSP. (Or C code in general, for that matter - the reason they have saturating addition and other oddball instructions is because it makes a lot of DSP code faster, but there's no C representation of any of those things.)
Post reply on HN