I was playing on the playground as always, and found one interesting feature.
In Rust, it's possible to dereference a null pointer and not get a segfault.
Now can your weak C++ do this??? No??? Rust wins
I was playing on the playground as always, and found one interesting feature.
In Rust, it's possible to dereference a null pointer and not get a segfault.
Now can your weak C++ do this??? No??? Rust wins
No, it's Undefined Behavior. UB may do anything, literally. This includes not crashing. UB is not "guaranteed crash/segfault".
You must never dereference a null pointer. Try running it in Miri. It reports:
note: inside `main`
--> src/main.rs:30:5
|
30 | x.deref_null();
| ^^^^^^^^^^^^^^
error: Undefined Behavior: dereferencing pointer failed: null pointer is a dangling pointer (it has no provenance)
--> src/main.rs:17:21
|
17 | *self = *(0usize as *const _);
| ^^^^^^^^^^^^^^^^^^^^^ dereferencing pointer failed: null pointer is a dangling pointer (it has no provenance)
|
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
= note: BACKTRACE:
= note: inside `UnsizedZST::deref_null` at src/main.rs:17:21: 17:42
note: inside `main`
--> src/main.rs:30:5
|
30 | x.deref_null();
Chill out, I just wanted to mention that dereference of a null pointer to a ZST generates no asm
By the way, do you know why doesn't the compiler actually make UnsizedZST
unsized because of the PhantomData<[()]>
?
// SAFETY: Deref of a nullptr to a ZST is being optimized out.
That's not even an optimization -- there's literally nothing to read. Even in debug mode, the LLVM IR just declares the pointer value in debuginfo, then returns.
; playground::UnsizedZST::deref_null
; Function Attrs: inlinehint nonlazybind uwtable
define internal void @_ZN10playground10UnsizedZST10deref_null17h2b88380cf2d7c90dE(ptr align 1 %self) unnamed_addr #0 !dbg !208 {
start:
%self.dbg.spill = alloca ptr, align 8
store ptr %self, ptr %self.dbg.spill, align 8
call void @llvm.dbg.declare(metadata ptr %self.dbg.spill, metadata !213, metadata !DIExpression()), !dbg !214
ret void, !dbg !215
}
Your program does not generate no assembly because of dereference of a null pointer.
It generates no assembly because it has no main()
function.
You should have:
pub fn main() {
instead of:
extern fn main() {
You're not right. I was using #![no_main]
to get more readable asm, it's not related to the result of the deref. If you use standard entry with std::rt
wrapper, nothing changes.
Well, it does not compile and run when extern
is used but does when changed to pub
. And then it prints your message ""UnsizedZST cannot be displayed bro".
So the assembler required to do that is generated.
PhantomData
is a magical type which is always a zero-sized Sized
value, no matter what its generic type is. It can't[1] be defined in user code.
Caveat: you kinda can emulate it with some weird hacks, e.g. the ghost crate, but ghost can't emulate the ?Sized
cover. âŠī¸
It actually compiles and generates
_ZN61_$LT$playground..UnsizedZST$u20$as$u20$core..fmt..Display$GT$3fmt17hf3771de73f49aafaE: # @"_ZN61_$LT$playground..UnsizedZST$u20$as$u20$core..fmt..Display$GT$3fmt17hf3771de73f49aafaE"
# %bb.0:
sub rsp, 56
mov rdi, rsi
lea rax, [rip + .L__unnamed_1]
mov qword ptr [rsp + 24], rax
mov qword ptr [rsp + 32], 1
mov qword ptr [rsp + 8], 0
lea rax, [rip + .L__unnamed_2]
mov qword ptr [rsp + 40], rax
mov qword ptr [rsp + 48], 0
lea rsi, [rsp + 8]
call qword ptr [rip + _ZN4core3fmt9Formatter9write_fmt17hc0f3e5f7fb738634E@GOTPCREL]
add rsp, 56
ret
# -- End function
main: # @main
# %bb.0:
sub rsp, 72
mov rax, rsp
mov qword ptr [rsp + 8], rax
mov rax, qword ptr [rip + _ZN61_$LT$playground..UnsizedZST$u20$as$u20$core..fmt..Display$GT$3fmt17hf3771de73f49aafaE@GOTPCREL]
mov qword ptr [rsp + 16], rax
lea rax, [rip + .L__unnamed_3]
mov qword ptr [rsp + 40], rax
mov qword ptr [rsp + 48], 2
mov qword ptr [rsp + 24], 0
lea rax, [rsp + 8]
mov qword ptr [rsp + 56], rax
mov qword ptr [rsp + 64], 1
lea rdi, [rsp + 24]
call qword ptr [rip + _ZN3std2io5stdio6_print17h710d5ba826a1c41eE@GOTPCREL]
add rsp, 72
ret
# -- End function
.L__unnamed_2:
.L__unnamed_4:
.ascii "__UnsizedZST cannot be displayed bro__"
.L__unnamed_1:
.quad .L__unnamed_4
.asciz "&\000\000\000\000\000\000"
.L__unnamed_5:
.byte 10
.L__unnamed_3:
.quad .L__unnamed_2
.zero 8
.quad .L__unnamed_5
.asciz "\001\000\000\000\000\000\000"
Hmm, OK. Yes it does.
But at no time have we used the result trying to deference a null pointer. So doing it with no code is an option when UB.
I'm not telling anyone to deref NULL
s, no. I just wanted to show some code work of which may be unobvious for some C and C++ coders. This thread was just a joke, I'm sorry if i actually wasted someone's time.
This'd probably be on-topic and appreciated at r/rustjerk. Unless stated otherwise, there's an assumption of genuineness on this forum.
Looks pretty funny, thanks. I don't use Reddit, unfortunately...
OK. No worries. I'm clearly too tired for such a subtle joke.
This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.