Live data from Hacker News

Unsafe Zig Is Safer Than Unsafe Rust

andrewkelley.me

71–80 of 105 posts

Re: Unsafe Zig Is Safer Than Unsafe Rust

#71
post #40

Earlier quoted context omitted.

That's for local variables. Microsoft and Linus Torvalds didn't like it, because it's a way to suddenly cause unexpected stack growth of arbitrary size. That feature was made optional in C++11, and Microsoft never implemented it.

FWIW Microsoft does have SAL annotations to do the same thing. For example fread's prototype is size_t fread( _Out_writes_bytes_(_ElementSize*_Count) void * _DstBuf, _In_ size_t _ElementSize, _In_ size_t _Count, _Inout_ FILE * _File ); https://docs.microsoft.com/en-us/visualstudio/code-quality/a...

C++ compilers also have references to arrays which can be abused in some cases:

    template  int read(int fd, char (&buf)[len]); // array size will be infered
    int read(int fd, char (&buf)[1024]); // array size must be exactly 1024

Re: Unsafe Zig Is Safer Than Unsafe Rust

#72
post #54

Earlier quoted context omitted.

Thanks! I'm not convinced that the statement in the paper translates into what you said: the key piece of that paragraph is "or seems to be". The Leakpocalypse problem was one piece of code (crossbeam's scoped threads API) was relying on an invariant that doesn't actually hold ("destructors will always run"). It was, fundamentally, a bug in the `unsafe` code in crossbeam, meaning it was incorrect for crossbeam to cal…

> To summarise, I still don't see how these two sentences are different: ... To "observe unsafe behavior" means I can write a program that does something safe, e.g., a data race or invalid memory access. It's possible to write library X and Y in such a way that I can observe unsafe behavior using both X and Y in my program, without putting "unsafe" blocks in my program. This is possible even if I can't do the same th…

I'm still not understanding: other than the hardware/global state thing in another comment in this thread[1], what's a program that demonstrates this "composing safe interfaces is unsafe" property? The example in the paper is not one, it was a bug for crossbeam to mark its API safe.

[1]: I'm ignoring this case, because it's somewhat completely impossible to solve: there's no way Rust (or any language) can control this situation. And, there's a strong argument in my mind that this sort of scenario should have an `unsafe` constructor or something, to act as an assertion from the programmer that they're guaranteeing unique access to the resource.

Re: Unsafe Zig Is Safer Than Unsafe Rust

#73
post #53

Earlier quoted context omitted.

This is pretty much tautological, and nobody's arguing this point. However, you have the benefit of being able to narrow your search scope to the parts of your code marked `unsafe` instead of the entire project. Most things don't need unsafe code. For the things that do, you must yourself uphold the invariant that all requirements of safety are being obeyed when transitioning out of an unsafe block. If you don't do t…

> However, you have the benefit of being able to narrow your search scope to the parts of your code marked `unsafe` instead of the entire project. I may be missing some context, but this is certainly not true in Rust. In order to understand whether an individual piece of code marked `unsafe` is actually correct, you need to examine the context in which it is run and in general you could have to examine a large sectio…

Yes, it's limited to the module. That's still limited, and a good reason to keep such modules small.

Re: Unsafe Zig Is Safer Than Unsafe Rust

#74
post #56

Earlier quoted context omitted.

You have restated your position, but it is still incorrect in the context of this discussion. Even your original statement of "R be[ing] arbitrary Rust code with no 'unsafe' blocks" is problematic: any Rust code is, very unavoidably, built upon a foundation of unsafe code. It has to be , because it's running on an "unsafe" processor. And yet, any safe Rust code in the core library (barring a safeness bug) is obviousl…

> ...is obviously safely composable with any other safe Rust code precisely because it obeys safety guarantees when transitioning from unsafe to safe. And what are those safety guarantees? This is the part where I see a lot of handwaving. > ...either R + X + Y is safe or one of [X, Y] has a safety bug and is inaccurately marking an unsafe interface as safe. Correct, but the problem is that we don't have a way to iden…

> And what are those safety guarantees?

It is still that we are still working this out; this is what we're cooprating with academia on, formalizing the exact semantics. Such things take time.

Re: Unsafe Zig Is Safer Than Unsafe Rust

#75
post #56

Earlier quoted context omitted.

You have restated your position, but it is still incorrect in the context of this discussion. Even your original statement of "R be[ing] arbitrary Rust code with no 'unsafe' blocks" is problematic: any Rust code is, very unavoidably, built upon a foundation of unsafe code. It has to be , because it's running on an "unsafe" processor. And yet, any safe Rust code in the core library (barring a safeness bug) is obviousl…

> ...is obviously safely composable with any other safe Rust code precisely because it obeys safety guarantees when transitioning from unsafe to safe. And what are those safety guarantees? This is the part where I see a lot of handwaving. > ...either R + X + Y is safe or one of [X, Y] has a safety bug and is inaccurately marking an unsafe interface as safe. Correct, but the problem is that we don't have a way to iden…

> And what are those safety guarantees? This is the part where I see a lot of handwaving.

I think this is the contention: correct me if I'm wrong, but you're saying, that, in practice, the safety guarantees of Rust are currently too nebulous to be able to be enforced reliably, whereas most other people in this thread are, I think, visualising the "platonic Rust"/post-RustBelt Rust where the currently vague conditions for safety have been tweaked as needed and proved correct, treating the current situation more like a "just" bug (and the success of RustBelt so far hints that this isn't vapourware/imagination, there's significant concrete progress towards it).

That is to say, most people are talking about the potential of Rust's safety, whereas you're talking about the reality, right now. I think both positions are reasonable to think about, but it obviously leads to confusion when the positions aren't distinguished in a discussion. (I also think that most people would agree with you about Rust right now: there isn't a definite set of safety rules, so it can be hard to work out whether "edge-cases" are correct or not.)

Re: Unsafe Zig Is Safer Than Unsafe Rust

#76

I think the Rust is not how you should write such a code. Why not start with the struct, and cast to a void* or a char* when C code requires it? I.e., the buggy example becomes: #[derive(Copy, Clone, Debug)] #[repr(C)] struct Foo { a: i32, b: i32, } fn main() { let mut array = [Foo { a: 0x01010101i32, b: 0x01010101i32 }; 256]; let foo = &mut array[0]; foo.a += 1; } The unsafe section isn't even required, and the effe…

While the approach you suggest usually works well, it doesn't in this case: FILE_NAME_INFO[1] uses a "flexible array member"[2] (although not the C99 version of it), of requiring a dynamically sized character array in the struct's allocation, and writing directly to the memory after a struct instance. The 'WCHAR FileName[1];' field at the end of the struct is just a placeholder to allow easy access to that character array, the length 1 is a lie.

[1]: https://msdn.microsoft.com/en-us/library/windows/desktop/aa3...

[2]: https://en.wikipedia.org/wiki/Flexible_array_member

Re: Unsafe Zig Is Safer Than Unsafe Rust

#77

I think the Rust is not how you should write such a code. Why not start with the struct, and cast to a void* or a char* when C code requires it? I.e., the buggy example becomes: #[derive(Copy, Clone, Debug)] #[repr(C)] struct Foo { a: i32, b: i32, } fn main() { let mut array = [Foo { a: 0x01010101i32, b: 0x01010101i32 }; 256]; let foo = &mut array[0]; foo.a += 1; } The unsafe section isn't even required, and the effe…

The function in question takes a pointer to a variable sized buffer that only starts with a struct. So your alternative won't work (the declared size of the struct only has room for a single character of filename). And there are certainly instances of this pattern where you really need to choose the size of the buffer at run time.

Re: Unsafe Zig Is Safer Than Unsafe Rust

#78
post #68

Earlier quoted context omitted.

> see GeneralizedNewtypeDeriving, which is considered unsafe even though it used to be safe This is wrong. GND plus TypeFamilies or some other extension in that vein used to be unsound when combined. It has since been fixed via the introduction of type roles. > Composed atomic operations are not atomic. Incidentally, Haskell also has this figured out via the STM monad.

I hadn't heard about the advancement with Roles, but it seems that GND is still prohibited in "Safe Haskell"?

You’re probably reading an old document - this restriction was removed with the introduction of roles. https://downloads.haskell.org/~ghc/7.8.4/docs/html/users_gui...

Roles were introduced in 7.8.something, and GND was added to Safe.

Re: Unsafe Zig Is Safer Than Unsafe Rust

#79
post #60

Earlier quoted context omitted.

C++ largely suffers from the same problems. Often a C++ programmer can write code which relies on iterators and containers which is quite safe and difficult to mess up, while for a variety of highly-specialized applications, mixtures of packed structs, pointer arithmetic, and arbitary sequences of binary data need to be handled with utmost care. Knowing when to use which set of tools and how to safely glue them toget…

Container and iterator code is not safe at all since there is no bounds checking by default and no protection against iterator invalidation, which can both cause writes to memory outside the intended object and thus a catastrophic outcome. There is no safe subset of C/C++ unless you just don't use pointers or references at all (and refrain from using any library that is not safe which includes large parts of the stan…

> unless you just don't use pointers or references at all (and refrain from using any library that is not safe which includes large parts of the standard library like all the containers)

It may seem far fetched, but it might be more practical than you'd think. The SaferCPlusPlus[1] library provides memory-safe implementations of the most commonly used standard library containers, and pointer types that reflect the lifetimes of their target objects. That is to say, there is a practical subset of C++ that is more closely comparable to safe Rust than is conventional C++.

[1] shameless plug: https://github.com/duneroadrunner/SaferCPlusPlus

Re: Unsafe Zig Is Safer Than Unsafe Rust

#80
Clearly both Rust and Zig tackle tough problems and implement solutions that will have trade offs. I don't think the top answer to a post talking about Zig's advantages should defensively try to point out how things could be different in Rust - if only you knew exactly what to do - instead it would be nice to see more discussion about other areas where Rust is perhaps better suited than Zig. For instance, you Rust clearly handles memory/pointers better (?), while maybe Zig is easier to learn?
Post reply on HN