15 ms·
From the table: > null pointer dereference ... [C] none; [Zig] runtime; [Rust] runtime Assuming this is talking normal "safe" Rust, I think I disagree with t
by quietbritishjim 6y ago
From the table:
> null pointer dereference ... [C] none; [Zig] runtime; [Rust] runtime
Assuming this is talking normal "safe" Rust, I think I disagree with this. The Rust analogue of a pointer in safe code is a reference, not a Rust pointer, and these can't null at all. You could use an Option<> of a reference, and Rust will internally use null to represent the None (empty) case, but an attept to use the option without checking for None will result in an error at compile time, not runtime. Yes you could convert that into a runtime error, but if it was an error condition for that variable to be None then (depending on the context) you could choose not to use an Option at all and then it would be a compile-time error at the call site to attempt to put None into it.
I don't think I understand what is meant by "type confusion". Surely this would also cause compile-time errors? Even C++ would give compile time errors for this unless you use a cast! (C, unlike C++, lets you implicitly convert from void* to any other pointer type so you don't need a cast to get pointer confusion.) Could someone think of an example of what might be meant here, and how it would cause a runtime error?
- the_duke 6y agoZig has a nullable pointer type, which will have null checks. But only in ReleaseSafe build mode, and it's trivial to construct a non nullable pointer that is null or uninitialised. So I agree with your disagreement.
- quietbritishjim 6y agoI'm not familiar with Zig, and I'm afraid I didn't quite follow your comment. Are you saying that "runtime" should instead be "none" for Zig?
- masklinn 6y agoDepends on the exact guarantees e.g. technically it's trivial to construct a nullable reference in Rust, but it's also `unsafe` and flagrantly UB so...
- jamii 6y ago> it's trivial to construct a non nullable pointer that is null or uninitialised This is checked at runtime in ReleaseSafe. jamie@machine:~$ cat test.zig pub fn main() void { var x: usize = 3; _ = @intToPtr(*u32, x - 3).*; } jamie@machine:~$ zig run test.zig -OReleaseSafe cast causes pointer to be null Aborted (core dumped) (The x-3 is because if you use a literal 0 it gets caught at compile time)
- creata 6y agoAnother (much smaller) detail is that signed overflow in C is undefined (and iirc GCC takes advantage of that when optimizing) but signed overflow in Rust is precisely defined to error in debug mode and wrap in release mode.
- edflsafoiewq 6y agoAnd unsigned overflow is also error-in-debug, overflow-in-release with Rust. If you want wrapping arithmetic, you have to ask for it. In C, unsigned overflow always wraps, so even though you can compile with overflow checks, there's no way to distinguish unsigned arithmetic that is supposed to vs not supposed to wrap.
- MaxBarraclough 6y agoSounds sensible. To quote John Regehr: > Java-style wrapping integers should never be the default, this is arguably even worse than C and C++’s UB-on-overflow which at least permits an implementation to trap. • https://blog.regehr.org/archives/1401 https://blog.regehr.org/archives/1401
- saagarjha 6y agoI actually dislike Rust's "wrap in production" default, tbh. It strikes a strange balance: "we care about performance in release mode but we are also going to check and make sure this code does specific things on overflow".
- tsimionescu 6y ago> I don't think I understand what is meant by "type confusion". Surely this would also cause compile-time errors? Even C++ would give compile time errors for this unless you use a cast! (C, unlike C++, lets you implicitly convert from void* to any other pointer type so you don't need a cast to get pointer confusion.) Could someone think of an example of what might be meant here, and how it would cause a runtime error? Given the foot-notes, I think they are referring to unions - probably writing to one union member but reading through another (e.g. uni.intVariant = 19; float a = uni.floatVariant). I don't personally know how Rust and Zig handle this,I believe it is UB in C.
- saagarjha 6y agoIt's actually defined as doing a type pun on the bit representation, interestingly. (The history of this specific behavior is somewhat complicated–it wasn't always clear-cut what this did.)
- quietbritishjim 6y ago[Edit: this isn't true] That is definitely how it is used, and it works on the major compilers. But, surprisingly, this is indeed undefined behaviour according to the standard. There was an infamous rant by Linus [1] (more infamous for the language than the subject) about how the kernel can and should continue to use unions for type punning. He noted that even though the C standard doesn't specify a behaviour for that usage, gcc does (so long as you don't use a particular compiler switch mentioned in that discussion), and went on to say "The standard simply is not important, when it is in direct conflict with reality and reliable code generation." EDIT: Having looked into this more I think I was getting confused with C++ (where the only supported way to type pun is to use memcpy or similar, which despite the name might not have to actually copy the bytes at runtime). Here [2] is a StackOverflow answer (/discussion) on the matter. [1] https://www.yodaiken.com/2018/06/07/torvalds-on-aliasing/ https://www.yodaiken.com/2018/06/07/torvalds-on-aliasing/ [2] https://stackoverflow.com/questions/25664848/unions-and-type-punning https://stackoverflow.com/questions/25664848/unions-and-type...
- 6y ago
- The_rationalist 6y agoI think it's fairly common in rust code to abuse unwrap and such behaving exactly like an unchecked access to a nullable type.
- Ygg2 6y agoThat's the opposite of true. Unwrap is heavily discouraged. I know for Result there is `?` operator, but I don't remember if that worked on Option.
- The_rationalist 6y agoThose are non mutually exclusive statements, it can be officially discouraged yet abused in practice because it often is for the developer the path of least resistance.
- johnisgood 6y agoHeavily discouraged by who? I have seen many Rust projects and they were all filled with unwraps. Two random Rust projects: https://github.com/tokio-rs/tokio https://github.com/tokio-rs/tokio: $ grep -irn 'unwrap' | wc -l 1398 https://github.com/denoland/deno https://github.com/denoland/deno: $ grep -irn 'unwrap' | wc -l 2050 I dunno, but all Rust projects I have encountered had unwraps everywhere.
- The_rationalist 6y agoThis really should get more attention. Safe null access in language like typescript or Kotlin through '?' syntax can still be abused through ('!!' ) but the incentive to abuse it is much lower than in rust because the safe syntax is just as short or shorter (in Kotlin)
- withoutboats2 6y agoThis is the opposite of reality: in Rust, the safe syntax `?` exists and there is no short syntax for the panicking form (`.unwrap()`). Users are not incentivized to unwrap in Rust, users are as disincitivized as possible to unwrap. The uses of unwrap in the projects the GP cited are overwhelmingly in tests and examples, which are not expected to handle errors in the same way as application code. The remainder are mainly lock poisoning unwrapping, which is a completely endorsed idiom.
- Blikkentrekker 6y ago> but an attept to use the option without checking for None will result in an error at compile time, not runtime. Yes you could convert that into a runtime error, but if it was an error condition for that variable to be None then (depending on the context) you could choose not to use an Option at all and then it would be a compile-time error at the call site to attempt to put None into it. This wording still suggests that it is a null pointer error reference, which is technically u.b., but in practice on any modern operating system a segmentation fault, but that is not what happens in Rust. Rather, an optional reference cannot be dereferenced at all ere it be converted to an actual reference, typically with appropriate guards for the None-case, it is true that there is a way to convert an optional value of type `T` to type `T` without handling the None-case, but that is simply a trivial library function that handles the None-case by panicking the thread. The None-case must technically always be handled in some way in safe code. The distinction is rather meaningful, I would say, as it is not the null pointer dereference stage where the error occurs, but the panicking of the thread of attempting to convert a None-case, which not only happens before that point, but provides for far cleaner debug information. In safe Rust, one simply cannot dereference an optional reference, one can only coerce it to a reference, with the understanding that this operation panics the thread in the None-case. — an optional reference in Rust is not a “reference” at all as it is in other languages with special support for it.
- jamii 6y ago> I don't think I understand what is meant by "type confusion". Accessing a memory location with one type as if it's another. In C or C++ it's usually because you accessed a union without checking a tag somewhere else. One classic way this happens is in an interpreter where you have a big enum for all the different possible data types: https://github.com/MaterializeInc/materialize/blob/1b9b3cfab699d2df18dcfa17a0880e88bb39b187/src/repr/src/scalar.rs#L28 https://github.com/MaterializeInc/materialize/blob/1b9b3cfab... And then in various builtin functions you expect particular types: https://github.com/MaterializeInc/materialize/blob/main/src/expr/src/scalar/func.rs#L758-L760 https://github.com/MaterializeInc/materialize/blob/main/src/... In rust and zig this code will produce a runtime error if you screw up, but in c it's easy to forget to check the tag and then you get UB. Similarly unwrap is checked in rust and zig but the equivalent in c - dereferencing a pointer that you are pretty sure is not null - is not. Of course rust and zig both have support for c-style unions too, but they're not the first thing people reach for.
- quietbritishjim 6y agoThanks for answering. As I said in another comment (in reply to another tsimionescu who suspected that's what you meant), I maintain that this is a compile time error. Yes, one usage is to panic if the contents of the enum is not what you expect, in which case an unexpected type causes a runtime error. But that is the programmer choosing to deal with it way. Fundamentally in the language, mismatching type is a compile time error. In many situations it's feasible to exhaustively match and deal with all possible cases of an enum (which then causes a compile time error if you try to reference one of the other inner types from within the "wrong" case). Where one particular case is expected, you can often ensure through the type system that this case is the only possibility by using that individual type directly rather than passing that enum around. In some situations its not feasible or it is feasible but the extra faff isn't worth the reward - but even then I don't think it justifies saying that the type confusion is detected in runtime in a comparison of languages.
- tsimionescu 6y ago> In rust and zig this code will produce a runtime error if you screw up, but in c it's easy to forget to check the tag and then you get UB. As pointed out elsewhere, somewhat surprisingly, this is not UB in C, though it is in C++. In C it is merely unspecified behavior, but perfectly safe if intended.