|
|
Subscribe / Log in / New account

Function Pointer cast

Function Pointer cast

Posted Jan 30, 2024 13:50 UTC (Tue) by khim (subscriber, #9252)
In reply to: Function Pointer cast by tialaramex
Parent article: Defining the Rust 2024 edition

> So you definitely can't make those, they're figments of the compiler's imagination.

No, they are not. You may write something like this:

fn main() {
    let add_or_sub = if std::env::args().len() % 2 == 0 { add } else { sub };
    println!("{}", add_or_sub(42, 2));
}

fn add(x: i32, y: i32) -> i32 {
    x + y
}

fn sub(x: i32, y: i32) -> i32 {
    x - y
}

How may add_or_sub variable even exist if function pointers are just a figments of the compiler's imagination?

> You can undoubtedly make a function pointer from an integer and have it work. But no, you probably cannot convince Miri that's OK.

I would consider it a defect in Miri and a pretty serious one. While Miri is not supposed to handle all possible Rust programs there are way to many APIs that assume that you may create function pointers from integers. But yeah, ultimately Miri is just a model of Rust, not the full Rust.


to post comments

Function Pointer cast

Posted Jan 30, 2024 15:52 UTC (Tue) by tialaramex (subscriber, #21167) [Link] (2 responses)

What you've got there is just a function pointer, of type fn(i32, i32) -> i32 Although Rust doesn't have the uh, exciting variety of integer coercions of C, it does have coercions, and in particular what your snippet does is coerce the two ZSTs (which are distinct types) into merely function pointers (which are compatible if the functions have the same signature). Watch what happens if we modify your code a little:
fn main() {
    let mut add_or_sub = add;
    add_or_sub = if std::env::args().len() % 2 == 0 { add } else { sub };
    println!("{}", add_or_sub(42, 2));
}
error[E0308]: mismatched types
 --> src/main.rs:3:68
  |
3 |     add_or_sub = if std::env::args().len() % 2 == 0 { add } else { sub };
  |                                                                    ^^^ expected fn item, found a different fn item
  |
  = note: expected fn item `fn(_, _) -> _ {add}`
             found fn item `fn(_, _) -> _ {sub}`
  = note: different fn items have unique types, even if their signatures are the same
  = help: consider casting both fn items to fn pointers using `as fn(i32, i32) -> i32`

Function Pointer cast

Posted Jan 30, 2024 16:59 UTC (Tue) by khim (subscriber, #9252) [Link] (1 responses)

But isn't the fact that you now have add_or_sub which is not “a mere function pointer” means that you actually can make these (with transmute) e.g.?

Function Pointer cast

Posted Jan 30, 2024 18:18 UTC (Tue) by tialaramex (subscriber, #21167) [Link]

In your program add_or_sub was a function pointer, specifically it was a function pointer to some function which takes two i32s and returns just one, fn(i32, i32) -> i32. The function pointer is probably (on modern computers) a 64-bit value which in practice is just the address in memory of some machine code.

But in my response add_or_sub is now a function item, a Zero Size variable which is always (in this particular case) our add function. ZSTs have a singular value, which is why we don't need storage space for them, the value of the (now mis-named) add_or_sub is always just add, a specific function we wrote to add numbers together. Even another function with not just the same signature, but exactly the same body and resulting machine code is a different function and so cannot be assigned to the (again, now misnamed) add_or_sub variable.

Transmute is a red herring, transmuting things into a ZST is silly. If you can *spell* the ZST then you're welcome to have one, since it has only a single value the compiler knows from the type what its value is, we don't need to "transmute" it. However like a lambda type these function items are unnameable, so you can't do that.

Function pointers are very nameable, and I'd guess you've been thinking about function pointers all along, but I wanted to emphasise that Rust does have (and make good use of) types signifying specifically the function, not just a pointer.

Yes I imagine you can take a magic integer (with some well chosen value) and transmute it into a function pointer, and (on a typical modern computer) you could call the function via that pointer and it'd work. I am not surprised that MIRI cannot justify this, and I think there aren't a lot cases where anybody has a good reason to do it, so if "Miri doesn't like it" dissuades someone from doing this that's probably good.


Copyright © 2025, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds