为什么Rust中的函数指针行为会根据函数指针的可变性而有所不同?

时间:2019-04-22 20:41:57

标签: function rust ffi

在Rust的结构体中存储指向函数的原始指针时,程序的行为可能会根据原始指针的可变性以意想不到的方式更改。

使用 public class Picker { static Vector3f startRay; static Vector3f endRay; public Picker() { startRay = new Vector3f(); endRay = new Vector3f(); } private static Vector2f getMouseCoords() { float x = (2f* Mouse.getX()) / Display.getWidth() - 1; float y = (2f * Mouse.getY()) / Display.getHeight() - 1f; return new Vector2f(x, y); } public static String getRayStr() { float x = (2f* Mouse.getX()) / Display.getWidth() - 1; float y = (2f * Mouse.getY()) / Display.getHeight() - 1f; String str = "Mouse X: " + x + " Mouse Y: " + y; str += "\nStart X: " + startRay.x + " Start Y: " + startRay.y + " Start Z: " + startRay.z; str += "\nEnd X: " + endRay.x + " End Y: " + endRay.y + " End Z: " + endRay.z; return str; } public static void setStartRay() { Vector2f mousePos = getMouseCoords(); Vector3f tempStartPos = new Vector3f(); tempStartPos.x = mousePos.x; tempStartPos.y = mousePos.y; tempStartPos.z = 0; startRay = tempStartPos; } public static void setEndRay() { Vector2f mousePos = getMouseCoords(); Vector3f tempEndPos = new Vector3f(); tempEndPos.x = mousePos.x; tempEndPos.y = mousePos.y; tempEndPos.z = -1; endRay = tempEndPos; } } 指针可以得到预期的结果。

以下代码也可以在playground上查看:

const

type ExternFn = unsafe extern "C" fn() -> (); unsafe extern "C" fn test_fn() { println!("Hello!"); } mod mut_ptr { use super::{ExternFn, test_fn}; #[derive(Debug, Eq, PartialEq)] pub struct FunctionHolder { function: *mut ExternFn, } impl FunctionHolder { pub fn new() -> Self { FunctionHolder { function: (&mut (test_fn as ExternFn) as *mut _), } } pub fn call(&self) { if !self.function.is_null() { unsafe { (&*self.function)(); } } } } } mod const_ptr { use super::{ExternFn, test_fn}; #[derive(Debug, Eq, PartialEq)] pub struct FunctionHolder { function: *const ExternFn, } impl FunctionHolder { pub fn new() -> Self { FunctionHolder { function: (&(test_fn as ExternFn) as *const _), } } pub fn call(&self) { if !self.function.is_null() { unsafe { (&*self.function)(); } } } } } // use const_ptr::FunctionHolder; use mut_ptr::FunctionHolder; fn check_holder(holder: &FunctionHolder) -> bool { let good = FunctionHolder::new(); println!("parameter = {:#?}", holder); println!("expected = {:#?}", good); holder == &good } fn main() { let f0 = FunctionHolder::new(); println!("{:?}", f0); let f1 = FunctionHolder::new(); println!("{:?}", f1); // uncomment this line to cause a segfault if using the // mut_ptr version :-( // f1.call(); assert!(check_holder(&f1)); } 模块中,代码的行为符合预期:无论在何处调用函数,并使用const_ptr方法,存储在FunctionHolder结构中的指针值都相同。调用所需的函数。

FunctionHolder::call模块中,存在一些意外的差异:

  • mut_ptr方法返回一个结构,该结构根据调用函数的不同而具有不同的值,

  • FunctionHolder::new方法导致段错误。

1 个答案:

答案 0 :(得分:5)

fn() -> ()是一个函数指针。 *const fn() -> ()*mut fn() -> ()功能指针

您想使用简单得多的代码,这也意味着这两种实现之间没有区别:

#[derive(Debug, Eq, PartialEq)]
pub struct FunctionHolder {
    function: Option<ExternFn>,
}

impl FunctionHolder {
    pub fn new() -> Self {
        FunctionHolder {
            function: Some(test_fn as ExternFn),
        }
    }

    pub fn call(&self) {
        if let Some(f) = self.function {
            unsafe { f(); }
        }
    }
}

如评论中所述,每次对文字值进行可变引用都会构造一个新值:

fn main() {
    println!("{:p}", &42);
    println!("{:p}", &42);
    println!("{:p}", &42);

    println!("{:p}", &mut 42);
    println!("{:p}", &mut 42);
    println!("{:p}", &mut 42);
}
0x55a551c03a34
0x55a551c03a34
0x55a551c03a34
0x7ffd40dbb95c
0x7ffd40dbb9bc
0x7ffd40dbba1c

对文字的不可变引用具有隐式static提升:

let a = &42;
// More-or-less
static HIDDEN: i32 = 42;
let a = &HIDDEN;

对字面量desugar的可变引用有效:

let mut hidden: i32 = 42;
let a = &mut hidden;

通过使用原始指针,您失去了借阅检查器的支持,无法指出您的引用的寿命不足以应对可变情况。

另请参阅: