gpt4 book ai didi

rust - 为什么 Rust 允许通过空指针调用函数?

转载 作者:行者123 更新时间:2023-12-02 09:42:21 25 4
gpt4 key购买 nike

我在 Rust 中尝试了函数指针魔术,最终得到了一个代码片段,我完全没有解释它为什么编译,甚至更多,为什么它运行。

fn foo() {
println!("This is really weird...");
}

fn caller<F>() where F: FnMut() {
let closure_ptr = 0 as *mut F;
let closure = unsafe { &mut *closure_ptr };
closure();
}

fn create<F>(_: F) where F: FnMut() {
caller::<F>();
}

fn main() {
create(foo);

create(|| println!("Okay..."));

let val = 42;
create(|| println!("This will seg fault: {}", val));
}
我无法解释为什么 foo正在通过在 caller(...) 中转换空指针来调用到 F 类型的实例.我原以为只能通过相应的函数指针调用函数,但鉴于指针本身为空,显然情况并非如此。话虽如此,似乎我显然误解了 Rust 类型系统的一个重要部分。
Example on Playground

最佳答案

这个程序实际上根本没有构造函数指针——它总是调用 foo和这两个直接关闭。
每个 Rust 函数,无论是闭包还是 fn item,具有唯一的匿名类型。这种类型实现了 Fn/FnMut/FnOnce特征,视情况而定。 fn 的匿名类型item 的大小为零,就像没有捕获的闭包类型一样。
因此,表达式 create(foo)实例化 create的参数 Ffoo的类型 - 这不是函数指针类型 fn() ,但是一个匿名的、零大小的类型,仅用于 foo .在错误消息中,rustc 调用此类型 fn() {foo} ,如您所见 this error message .
create::<fn() {foo}> (使用错误消息中的名称),表达式 caller::<F>()将此类型转发到 caller而不给它一个该类型的值。
最后,在 caller::<fn() {foo}>表达式 closure()脱糖至 FnMut::call_mut(closure) .因为 closure有类型 &mut F哪里F只是零尺寸类型 fn() {foo} , 0 closure 的值本身根本就没有被使用过1,并且程序调用了foo直接地。
同样的逻辑适用于闭包 || println!("Okay...") , 喜欢 foo有一个匿名的零大小类型,这次叫做 [closure@src/main.rs:2:14: 2:36] .
第二个闭包就没有那么幸运了——它的类型不是零大小的,因为它必须包含对变量 val 的引用。 .这一次,FnMut::call_mut(closure)实际上需要取消引用 closure做它的工作。所以它崩溃了2。

1 像这样构造一个空引用在技术上是未定义的行为,因此编译器不对该程序的整体行为作出 promise 。但是,替换 0与其他一些“地址”对齐 F避免了零大小类型的问题,例如 fn() {foo} ,并给出 the same behavior !)
2 同样,构造一个空(或悬空)引用是实际承担责任的操作 - 之后,任何事情都会发生。段错误只是一种可能性 - rustc 的 future 版本,或者在稍微不同的程序上运行时的相同版本,可能会完全做其他事情!

关于rust - 为什么 Rust 允许通过空指针调用函数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/63164973/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com