Why don't Box<Fn> and &mut Fn implement Fn like &Fn does?

I was responding to an issue on github when it dawned upon me that I really have no idea why Box<Fn()> doesn't implement Fn(). I think I worked around this once by just making my own fake Fn trait, which is really kind of dumb if you think about it.

I’m guessing it’s because you’d then have to make Box<Fn()> also implement FnOnce but you cannot implement that for Box<Fn()> because you cannot move the Fn as a self since it’s unsized - see FnBox as a workaround.

Says who? (playpen)

impl<'a, A, R> Fn<A> for &'a Fn<A, Output=R> { }
impl<'a, A, R> FnMut<A> for &'a Fn<A, Output=R> { }
impl<'a, A, R> FnOnce<A> for &'a Fn<A, Output=R> { }

impl<'a, A, R> Fn<A> for &'a mut Fn<A, Output=R> { }
impl<'a, A, R> FnMut<A> for &'a mut Fn<A, Output=R> { }
impl<'a, A, R> FnOnce<A> for &'a mut Fn<A, Output=R> { }

impl<A, R> Fn<A> for Box<Fn<A, Output=R>> { }
impl<A, R> FnMut<A> for Box<Fn<A, Output=R>> { }
impl<A, R> FnOnce<A> for Box<Fn<A, Output=R>> { }

impl<'a, A, R> FnMut<A> for &'a mut FnMut<A, Output=R> { }
impl<'a, A, R> FnOnce<A> for &'a mut FnMut<A, Output=R> { }

impl<A, R> FnMut<A> for Box<FnMut<A, Output=R>> { }
impl<A, R> FnOnce<A> for Box<FnMut<A, Output=R>> { }

// no impl<A, R> FnOnce<A> for Box<FnOnce<A, Output=R>> { },
// but everything else works fine!

Edit: I subtly renamed the topic in response to the fact that impl Fn for &mut Fn apparently does not exist either. (it used to read "why doesn't Box<Fn> implement Fn like &Fn and &mut Fn do?")

1 Like

What you wrote there for the boxed versions is nothing but trait objects, which is not what you’d want in std - you’d want, for example, impl<A, F: Fn<A> + ?Sized> Fn<A> for Box<F>. Once you get to FnOnce you cannot do it if it’s ?Sized.

I also think there’s a separate coherence issue with FnOnce being implemented for Box - it would conflict with impl<A, F: FnOnce<A> + ?Sized> FnOnce<A> for Box<F>.

It's a coherence issue. I think removing FnBox resolves it, but I'm not sure. I know I've tried to add the Box<Fn>, Box<FnMut> impls but it didn't compile at that time.

Would love for the basic case of;

let foo: Box<FnOnce()> = ...
foo()

to just work. FnBox is just too much of a bad hack from users point and user doesn't care about how compiler makes it happen.

The coherence that @vitalyd mentions is possibly solved with specialization.

Had a simple language addition idea rolling in my head for months that would workaround the Box not working in your example.
Simply put it would allow you to identify the type that is normally by default anonymous; one for functions( & closures) and one the return value.

fn wrap(b: Box<Fn(&ShaderName) -> ShaderDefinitionFuture>) -> impl Fn(&ShaderName) -> ShaderDefinitionFuture {
    |sn| b(sn)
}
pub type ShaderLoader = -> wrap;

Oops, I must be blind! I somehow missed the fact that those impls in std are generic over F.

But here's another sample. Here, I add four new impls on top of those already existing in std.

The important thing here is that I've made the impl for Box<FnOnce> require FnMut instead of FnOnce.

impl<'a, A, F> Fn<A> for &'a mut F 
where F: Fn<A> + ?Sized { ... }

impl<A, F> Fn<A> for Box<F>
where F: Fn<A> + ?Sized { ... } 
   
impl<A, F> FnMut<A> for Box<F>
where F: FnMut<A> + ?Sized { ... }
    
// NOTE: This one has weaker bounds than ideally possible
//       due to the aforementioned problems with ?Sized.
impl<A, F> FnOnce<A> for Box<F>
where F: FnMut<A> + ?Sized { ... }

"Desirable" impls

Consider the kinds of impls that would be desirable (even if not possible due to the precise signatures of the Fn traits) given these typical "effective meanings:"

  • Closures:
    • Suppose MyFnOnce does not impl FnMut. Then you must give up ownership to call it.
    • Suppose MyFnMut does not impl Fn. Then you require exclusive access to call it.
  • Asking for a generic function:
    • Request F: FnOnce if you only need to call it once.
    • Request F: FnMut if you need to call it multiple times.
    • Request F: Fn if you need to call it while also sharing it.
  • Supplying a type-erased closure to another API:
    • Supply Box::new(f) to relinquish ownership.
    • Supply &mut f to give exclusive access.
    • Supply &f to give shared access.

And suppose we have the following functions for testing if a trait is implemented:

// These functions test if the argument impls a Fn trait.
fn is_fn<F: Fn<(), Output=()>>(_: F) {}
fn is_mut<F: FnMut<(), Output=()>>(_: F) {}
fn is_once<F: FnOnce<(), Output=()>>(_: F) {}

Then here's a summary of all the things we would like to be able to do, and whether they work with todays impls or with the additional impls I presented above.

Call Possible ever Possible today Possible with the extra impls
is_fn(&MyFn) x x x
is_mut(&MyFn) x x x
is_once(&MyFn) x x x
is_fn(&mut MyFn) x -- x
is_mut(&mut MyFn) x x x
is_once(&mut MyFn) x x x
is_fn(Box::new(MyFn)) x -- x
is_mut(Box::new(MyFn)) x -- x
is_once(Box::new(MyFn)) x -- x
Call Possible ever Possible today Possible with the extra impls
is_mut(&mut MyFnMut) x x x
is_once(&mut MyFnMut) x x x
is_mut(Box::new(MyFnMut)) x -- x
is_once(Box::new(MyFnMut)) x -- x
Call Possible ever Possible today Possible with the extra impls
is_once(Box::new(MyFnOnce)) -- -- --

This runs into the coherence issue with FnBox if you were to do this in std. I think we need to figure out what to do with it, ideally get rid of it.

My understanding is the compiler team doesn’t want to make any more special accommodations for Box; if they did then it’s likely Box<FnOnce> could be made to work and FnBox could go away. At that point, and with specialization, a more flexible design could be made (I think).

1 Like