Unsafe fn with type parameter is not treated as unsafe fn in trait resolution

Assuming we have trait implementations like:

impl Deleter for unsafe fn(*mut u8) {
    fn delete<T>(&mut self, ptr: *mut u8) {
        unsafe {
            (*self)(ptr)
        }
    }
}

impl<F: FnMut(*mut u8)> Deleter for F {
    fn delete<T>(&mut self, ptr: *mut u8) {
        (*self)(ptr)
    }
}

When we try to pass function like:

pub unsafe fn default_deleter<T>(ptr: *mut u8) {
    alloc::boxed::Box::from_raw(ptr as *mut T);
}

Would result in selecting FnMut implementation, not unsafe fn
Does it looke like an oversight?
From my perspective even type bound fn is still fn.

When compiling generic that accepts Deleter:

error[E0277]: expected a `std::ops::FnMut<(*mut u8,)>` closure, found `unsafe fn(*mut u8) {smart_ptr::default_deleter::<should_dtor_global::MyDeleter<'_>>}`
  --> tests\unique.rs:64:66
   |
64 |         Unique::new(Box::leak(var) as *mut MyDeleter as *mut u8, smart_ptr::default_deleter::<MyDeleter>);
   |                                                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected an `FnMut<(*mut u8,)>` closure, found `unsafe fn(*mut u8) {smart_ptr::default_deleter::<should_dtor_global::MyDeleter<'_>>}`
   |

I'm not quite sure what the details of the situation are, but you should note that referring to a function gives you a special zero sized type specific to that very function that implements FnMut. This is just like how every closure has its own type. However this special zero sized type can coerce to a value of type unsafe fn (*mut u8).

The type of a function is an unnamed type that coerces into a function pointer but is not one, and coercions don't happen at generic calls when the type is inferred.

A minimal example is

trait MyFn {}
impl MyFn for fn() {}

fn foo<F: MyFn>(_: F) {}

pub fn bar() {
    //foo(bar); // doesn't work
    foo::<fn()>(bar); // works
}
1 Like

However this special zero sized type can coerce to a value of type unsafe fn (*mut u8) .

Yeah, this is what I expected, because plain functions coerce just fine, but not when I add type parameter to it.
Which was kinda confusing and made me wondering if it is some sorta oversight

1 Like