Hacker News new | ask | show | jobs
by gauntletstricky 495 days ago
>In that sense, Rust is indeed specifically designed to play well in C codebases as the rest of the C code can basically pretend it's only working with C code.

Do the semantics of the borrow checker play nicely with C projects, depending on architectures? And the other semantics of Rust?

https://www.reddit.com/r/rust/comments/90s2no/rust_c_interop...

>When we started this, even rust itself was not ready and over this time rust has improved and it is today a better language and it is better prepared to offer something like this. For us or for other projects.

>It takes someone who is interested and good at both languages to dig in, understand the architectures, the challenges and the protocols to drive this all the way through.

>I am not against revisiting the topic and the idea of providing alternative backends for HTTP/1 in the future, but I think we should proceed a little different next time. We also have a better internal architecture now to build on than what we had in 2020 when this attempt started.

If the above quotes are accurate, how would it be consistent with what you wrote?

>In that sense, Rust is indeed specifically designed to play well in C codebases as the rest of the C code can basically pretend it's only working with C code.

This quote especially:

>It takes someone who is interested and good at both languages to dig in, (...)

As for being designed for interop with C, I found this blog:

https://ludwigabap.bearblog.dev/zig-vs-rust-at-work-the-choi...

Where they chose Zig instead of Rust, and mentioned the importance of interop, and that Zig also is a C compiler. Something that is claimed on Zig's website as well:

>Incrementally improve your C/C++/Zig codebase. > >Use Zig as a zero-dependency, drop-in C/C++ compiler that supports cross-compilation out-of-the-box.

I know barely anything about Zig, so I don't know the veracity of this, however.

Rust's FFI also relies on undefined behavior if this comment isn't outdated https://github.com/rust-lang/rust/pull/59625#issuecomment-48... https://github.com/rust-lang/rust/blob/master/library/core/s... . Might not be an issue as long as the implementation is only used with LLVM and LLVM doesn't change.

2 comments

Borrow checker semantics only apply to Rust, not C. When you write the bindings you're also responsible for mapping the C code's preconditions and postconditions to something the borrow checker can understand.

For example, the semi-common pattern of reference-counted objects sitting behind opaque pointers would call for a transparent wrapper type around a bare pointer, with appropriate implementations of Drop and Clone to decrement and increment the reference count, and inlined wrapper methods to call any C methods associated with the object.

You can, of course, call C directly without worrying about writing any of those types; but now you have to write a bunch of unsafe blocks to call those functions all over your non-binding code. This isn't great. In fact, that's basically the underlying argument surrounding R4L right now: the Rust people want to write nice Rust bindings that let the borrow checker do the work, while the C people[0] want Rust to just call C directly. To be clear, both approaches have the same runtime performance, the only implication is on whether or not Rust will be able to verify correctness of the code.

The unsoundness you mentioned specifically refers to taking variadic arguments when called from C, which are a bit of a hack even before you talk about Rust interop. In ISO C, you have to call va_end in the current function, with the current stack frame. Rust's compiler doesn't guarantee that Drop implementations are inlined, so your code might wind up calling something else that calls va_end, which isn't good enough. However, this is a moot point, because LLVM/clang treat va_end as a no-op and thus can't get screwed up by Rust not following ISO C spec. I doubt LLVM is going to add optimizer passes specifically to break code that uses va_end wrong if they aren't even using it themselves. If they did, rustc could be modified specifically to support forcibly inlined Drop, just for std::ffi, if needs be. It wouldn't break any code to add this. In other words, it doesn't matter unless you're trying to make Rust code link with, say, stuff compiled by ancient C compilers like Metrowerks CodeWarrior[1] or something; and that stuff makes use of varargs.

As for Zig, I haven't used it. I trust that it also has good interop facilities. But that doesn't matter since we're talking about Rust for Linux, not Zig for Linux.

[0] Or at least the ones willing to entertain a multi-language Linux

[1] I have no clue if that compiler ships a va_end that depends on being called in the current stack frame or not.

>In fact, that's basically the underlying argument surrounding R4L right now: the Rust people want to write nice Rust bindings that let the borrow checker do the work, while the C people[0] want Rust to just call C directly.

But the bindings still require expertise in both languages to write, don't they? And knowledge of both codebases?

And I haven't followed the specifics of the mailing list debate closely, but couldn't the nice bindings be copied or vendored into each Rust driver or something? I could be wrong, and it wouldn't be neat at all, and be a lot of bloat, but would enable having bindings. Some people argued the Rust code could be maintained out-of-tree, like some other projects were, even though it would take extra effort and also probably require tooling and processes.

>In other words, it doesn't matter unless you're trying to make Rust code link with, say, stuff compiled by ancient C compilers like Metrowerks CodeWarrior[1] or something; and that stuff makes use of varargs.

I did mention that it might not be an issue in practice, given some assumptions. However, if LLVM changes, or if for instance the Rust standard library is reused or copied over to GCC Rust, then the code would need to be changed. For GCC Rust, I wonder if part or all of the standard library could be forked, to have a GCC specific Rust standard library implementation, similar to what happens with C++'s standard library.

>As for Zig, I haven't used it. I trust that it also has good interop facilities. But that doesn't matter since we're talking about Rust for Linux, not Zig for Linux.

Sorry, but it does matter, since claiming that a language like Rust has good C interop of course leads to relative comparisons with other languages in similar spaces and how well they do relative to Rust. And if the promises by Zig holds, and the experience people talk about with Zig holds, then Zig appears to have a substantially better track record in regards to C interop than Rust. Which isn't surprising, since Zig is a less advanced and more primitive language than Rust, and while that has drawbacks, it can also have benefits, possibly like in this case with having much better C interop than Rust. In the same vein that the interop between Rust and C is likely way better than (unless confined to the C subset) the interop between Rust and C++.

> Do the semantics of the borrow checker play nicely with C projects, depending on architectures? And the other semantics of Rust?

I don't see why the borrow checker and other Rust semantics are relevant in this context. As the sibling comment stated, if you're writing Rust code that is intended to be consumed by C code you're going to be writing a C API, which almost certainly entails mapping Rust semantics to C semantics and vice-versa so what you expose is what other C code can understand (i.e., you're not exposing anything Rust-specific). Sure, that might require the calling C code to do some work on their end to uphold invariants, but that is not exactly an uncommon requirement when writing C in general.

> If the above quotes are accurate, how would it be consistent with what you wrote?

Well, first of all you're stripping context from the bit you quoted from me. That statement was contrasting Rust's C interop with languages with runtimes which require the consuming C code to be aware of the fact that they're calling into a different language and require additional boilerplate to do so - messing with refcounts in Python, JVM-specific types in Java, so on and so forth. In this context, the quotes you picked out are basically irrelevant as they are about entirely different topics.

But in any case, let's try to look at those quotes from the article anyways:

>When we started this, even rust itself was not ready and over this time rust has improved and it is today a better language and it is better prepared to offer something like this. For us or for other projects.

"Rust was designed to play well in C codebases" is not inconsistent with "Rust's C interop can be improved".

Unfortunately it's been hard to find exactly what Rust changes have been driven by this work, but at least based on the initial announcement of the hyper backend experiment [0] it seems that the improvements to Rust are less about the language's C interop and more about dealing with error handling/OOM. An important issue indeed, and one that is still being worked on, but distinct from language-level C interop nevertheless.

Put another way, there would be similar issues if someone tried to add a C backend which aborted on OOM as well. It's not "Rust doesn't play well with C", it's "this development style doesn't match the one we want/use". That is language-agnostic.

>It takes someone who is interested and good at both languages to dig in, understand the architectures, the challenges and the protocols to drive this all the way through.

I can't say I understand your focus on this quote. There's no inconsistency between this and what I said because this quote says nothing about how well (or not) the Rust code works with the rest of the curl codebase. The quote would apply basically verbatim to C++/Zig/D as well and those have nearly seamless C interop.

>I am not against revisiting the topic and the idea of providing alternative backends for HTTP/1 in the future, but I think we should proceed a little different next time. We also have a better internal architecture now to build on than what we had in 2020 when this attempt started.

Similar thing here.

> As for being designed for interop with C, I found this blog: <link> Where they chose Zig instead of Rust, and mentioned the importance of interop, and that Zig also is a C compiler. Something that is claimed on Zig's website as well:

Zig does indeed have very nice interop with C. However, that's irrelevant in the context of this conversation - the fact that Zig (or other languages like C++ and D, for example) is better at C interop than Rust has literally zero bearing on whether Rust was designed for interop with C.

> Rust's FFI also relies on undefined behavior if this comment isn't outdated

I'm not sure the comment isn't outdated, but in any case I think the sibling comment addresses this better than I could.

This is one of those things where I suspect the current behavior is good enough given current manpower/resource constraints and would be reprioritized if it ever becomes an issue.

[0]: https://daniel.haxx.se/blog/2020/10/09/rust-in-curl-with-hyp...

Your whole comment basically says that I'm right and that you were wrong, while portraying it as the opposite. It's really weird.