Why does Rust allow calling functions via null pointers?

前端 未结 5 1036
臣服心动
臣服心动 2021-02-05 00:21

I was experimenting with function pointer magic in Rust and ended up with a code snippet which I have absolutely no explanation for why it compiles and even more, why it runs.

5条回答
  •  忘掉有多难
    2021-02-05 00:48

    The type of fn foo() {...} is not a function pointer fn(), it's actually a unique type specific to foo. As long as you carry that type along (here as F), the compiler knows how to call it without needing any extra pointers (a value of such a type carries no data). A closure that doesn't capture anything works the same way. It only gets dicey when the last closure tries to look up val because you put a 0 where (presumably) the pointer to val was supposed to be.

    You can observe this with size_of, in the first two calls, the size of closure is zero, but in the last call with something captured in the closure, the size is 8 (at least on the playground). If the size is 0, the program doesn't have to load anything from the NULL pointer.

    The effective cast of a NULL pointer to a reference is still undefined behavior, but because of type shenanigans and not because of memory access shenanigans: having references that are really NULL is in itself illegal, because memory layout of types like Option<&T> relies on the assumption that the value of a reference is never NULL. Here's an example of how it can go wrong:

    unsafe fn null(_: T) -> &'static mut T {
        &mut *(0 as *mut T)
    }
    
    fn foo() {
        println!("Hello, world!");
    }
    
    fn main() {
        unsafe {
            let x = null(foo);
            x(); // prints "Hello, world!"
            let y = Some(x);
            println!("{:?}", y.is_some()); // prints "false", y is None!
        }
    }
    

提交回复
热议问题