Hacker News new | ask | show | jobs
by hermanradtke 455 days ago
> basically written in C

Unsafe Rust still has to conform to many of Rust’s rules. It is meaningfully different than C.

2 comments

It has also way less tooling available than C to analyze its safety.
The things I’ve seen broadly adopted in the industry (i.e. sanitizers) are equally available in Rust. & Rust’s testing infrastructure is standardized so tests are actually common to see in every library.
The number of tools matters less than the quality of the tools. Rust’s inherent guarantees + miri + software verification tools mean that in practice Rust code, even with unsafe, ends up being higher quality.
Miri is better than any C tool I'm aware of for runtime UB detection.
Miri is the closest to a UB specification for Rust that there is, coming in the form of a tool so you can run it. It's really cool but Valgrind, which is a C tool that also supports Rust, also supports Rust code that calls to C and that does I/O, both pretty common things for programs to do.
Are there examples you're thinking about? The only good ones I can think of are bits about undefined behavior semantics, which frankly are very well covered in modern C code via tools like ubsan, etc...
They're just fundamentally different languages. There's semantics that exist in all four of these quadrants:

* defined in C, undefined in Rust

* undefined in C, undefined in Rust

* defined in Rust, undefined in C

* defined in Rust, defined in C

That doesn't seem responsive. The question wasn't whether Rust and C are literally the same language ("duh", as it were), it was effectively "are there meaningful safety features provided to the unsafe zlib-rs code in question in that aren't already available in C toolchains/ecosystems?"

And there really aren't. The abbreviated/limited safety environment being exploited by this non-idiomatic Rust code seems to me to be basically isomorphic to the way you'd solve the problem in C.

> it was effectively "are there meaningful safety features provided to the unsafe zlib-rs code in question in that aren't already available in C toolchains/ecosystems?"

Ah, so that was like, not in your comment, but in a parent.

> And there really aren't.

I mean, not all of the code is unsafe. From a cursory glance, there's surely way more here than I see in most Rust packages, but that doesn't mean that you get no advantages. I picked a random file, and chose some random code out of it, and see this:

    pub fn copy<'a>(
        dest: &mut MaybeUninit<DeflateStream<'a>>,
        source: &mut DeflateStream<'a>,
    ) -> ReturnCode {
        // SAFETY: source and dest are both mutable references, so guaranteed not to overlap.
        // dest being a reference to maybe uninitialized memory makes a copy of 1 DeflateStream valid.
        unsafe {
            core::ptr::copy_nonoverlapping(source, dest.as_mut_ptr(), 1);
        }
The semantics of safe code, `&mut T`, provide the justification for why the unsafe code is okay. Heck, this code wouldn't even be legal in C, thanks to strict aliasing. (Well, I guess you could argue that in C code they'd be of the same type, since you don't have "might be uninitialized" in C's typesystem, but again, this is an invariant encoded in the type system that C can't do, so it's not possible to express in C for that reason either.)
Isn't that exactly my point though? This is just a memcpy(). In C, you do some analysis to prove to yourself that the pointers are valid[1]. In this unsafe Rust code, the author did some analysis to prove the same thing. I mean, sure, the specific analyses use words and jargon that are different. I don't think that's particularly notable. This is C code, written in Rust.

[1] FWIW, memcpy() arguments are declared restrict post-C99, the strict aliasing thing doesn't apply, for exactly the reason you're imagining.

> In C, you do some analysis to prove to yourself that the pointers are valid[1]

Right, and in Rust, you don't have to do it yourself: the language does it for you. If the signature were in C, you'd have to analyze the callers to make sure that this property is upheld when invoked. In Rust, the compiler does that for you.

> the strict aliasing thing doesn't apply

Yes, this is the case in this specific instance due to it being literally memcpy, but if it were any other function with the same signature, the problem would exist. Again, I picked some code at random, I'm not saying this one specific instance is even the best one. The broader point of "Rust has a type system that lets you encode more invariants than C's" is still broadly true.

This comment summarizes the difference of unsafe Rust quite well. Basically, mostly safe Rust, but with few exceptions, fewer than one would imagine: https://news.ycombinator.com/item?id=43382176