为什么函数指针的行为在 Rust 中会根据函数指针的可变性而有所不同?
Why does function pointer behaviour differ in Rust depending on the mutability of the function pointer?
在 Rust 的结构中存储指向函数的原始指针时,程序的行为可能会根据原始指针的可变性以意想不到的方式发生变化。
使用 const
指针给出了预期的结果。
下面的代码也可以在playground上查看:
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
方法 returns 一个根据调用它的函数保存不同值的结构,
FunctionHolder::call
方法导致段错误。
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;
对文字的可变引用有效地脱糖:
let mut hidden: i32 = 42;
let a = &mut hidden;
通过使用原始指针,您将失去借用检查器的支持,无法指出您的引用对于可变情况而言寿命不够长。
另请参阅:
在 Rust 的结构中存储指向函数的原始指针时,程序的行为可能会根据原始指针的可变性以意想不到的方式发生变化。
使用 const
指针给出了预期的结果。
下面的代码也可以在playground上查看:
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
方法 returns 一个根据调用它的函数保存不同值的结构,FunctionHolder::call
方法导致段错误。
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;
对文字的可变引用有效地脱糖:
let mut hidden: i32 = 42;
let a = &mut hidden;
通过使用原始指针,您将失去借用检查器的支持,无法指出您的引用对于可变情况而言寿命不够长。
另请参阅: