Skip to content

Rules around call resolution on fn_traits are confusing #120022

Open
@Kolsky

Description

@Kolsky

I tried this code:

#![feature(fn_traits, unboxed_closures)]

pub struct Foo<F>(F); // a simple newtype over closure object, nothing more

impl<F: FnOnce(A) -> B, A, B> FnOnce<(A,)> for Foo<F> {
    type Output = B;

    extern "rust-call" fn call_once(self, (a,): (A,)) -> B {
        (self.0)(a)
    }
}

impl<F: FnMut(A) -> B, A, B> FnMut<(A,)> for Foo<F> {
    extern "rust-call" fn call_mut(&mut self, (a,): (A,)) -> B {
        (&mut self.0)(a)
    }
}

impl<F: Fn(A) -> B, A, B> Fn<(A,)> for Foo<F> {
    extern "rust-call" fn call(&self, (a,): (A,)) -> B {
        (&self.0)(a)
    }
}

fn main() {
    let mut s = String::new();
    
    // This works
    {
        let mut closure = |()| s.push_str("");
        (move |x| closure(x))(());
    }
    
    // This doesn't!
    {
        let closure = |()| s.push_str("");
        (Foo(closure))(());
    }
}

(playground)

I expected to see this happen: FnMut gets picked up as the next candidate fitting for closure call, code compiles succesfully.

Instead, this happened: the closest candidate in method resolution should be Self, if anything, but, evidently, &Self is preferred instead. It would be justifiable for closures specifically if the code could be compiled. It doesn't seem to align with anything known so far about either closures or method call expressions. Removing Fn impl makes the code compile successfully, but it shouldn't be considered at all due to unsatisfied F: Fn bound.

Meta

rustc version: 1.76.0 nightly

Main tracking issue: #29625

Metadata

Metadata

Assignees

No one assigned

    Labels

    C-discussionCategory: Discussion or questions that doesn't represent real issues.F-unboxed_closures`#![feature(unboxed_closures)]`T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.requires-nightlyThis issue requires a nightly compiler in some way.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions