Skip to content

Implementation of traits on functions ignored after function call #31730

Open

Description

@sophiajt

In this example, I implement a trait on generic functions. One with mutability and one without. When invoked manually, the proper trait gets called. Instead, if I try to peel off the function's unique tag using another function, the traits are no longer visible.

Checked against: rustc 1.8.0-nightly (57c357d 2016-02-16)

trait FooFn {
    fn foo(&self);
}

impl<T: Sized> FooFn for fn(&mut T)-> T {
    fn foo(&self) {
        println!("Generic (with mutable) match!");
    }
}


impl<T: Sized> FooFn for fn(T)->T {
    fn foo(&self) {
        println!("Generic match!");

    }    
}

fn simple_fn(x: i32) -> i32 { 1 }
fn simple_fn2(x: &mut i32) -> i32 { 2 }

fn strip<T, U>(x: fn(T)->U) -> fn(T)->U { x }

fn main() {
    //Works
    &(simple_fn as fn(i32)->i32).foo(); 
    //Also works
    &(simple_fn2 as fn(&mut i32)->i32).foo();

    //Works
    strip(simple_fn).foo();
    //Error: no method named `foo` found for type `fn(&mut i32) -> i32`
    strip(simple_fn2).foo();
}

Activity

  1. arielb1 commented on Feb 17, 2016

    @arielb1
    Contributor

    This is an unfortunate issue with late-bound regions.

    Your impl is for the type for<'a> fn(&'a mut T) -> T, while strip forces T to be concrete, so you have fn(&'α mut T) -> T for some (specific) lifetime α.

    I agree that the error message could be better.

  2. sophiajt commented on Feb 17, 2016

    @sophiajt
    ContributorAuthor

    @arielb1 Assuming I understand what you're saying, it seems like the more specific lifetime still inhabits the space of the more generic lifetimes that the impl covers. If that's true, I'd expect that functionality to still be available, even when it's concrete.

    Or are you saying that mechanically the decision of whether the type has the impl available to it has to be made earlier than the nailing down of the lifetime?

  3. eefriedman commented on Feb 17, 2016

    @eefriedman
    Contributor

    The issue is fundamentally a type mismatch; there are two different kinds of "generic" lifetime. It tends to be a bit confusing, since the syntax is very similar, and there are implicit conversions in some cases, but the error is legitimate.

    To illustrate:

    trait FooFn {
        fn foo(&self);
    }
    
    impl FooFn for for<'a> fn(&'a mut i32)-> i32 {
        fn foo(&self) {
            println!("Generic (with mutable) match!");
        }
    }
    
    impl<'a> FooFn for fn(&'a mut i32)-> i32 {
        fn foo(&self) {
            println!("Generic lifetime match!");
        }
    }
    
    impl FooFn for fn(i32)->i32 {
        fn foo(&self) {
            println!("Generic match!");
    
        }    
    }
    
    fn simple_fn(x: i32) -> i32 { 1 }
    fn simple_fn2(x: &mut i32) -> i32 { 2 }
    
    fn strip(x: fn(i32)->i32) -> fn(i32)->i32 { x }
    fn strip2(x: fn(&mut i32)->i32) -> fn(&mut i32)->i32 { x }
    fn strip3<'a>(x: fn(&'a mut i32)->i32) -> fn(&'a mut i32)->i32 { x }
    fn strip4(x: fn(&'static mut i32)->i32) -> fn(&'static mut i32)->i32 { x }
    
    fn main() {
        &(simple_fn as fn(i32)->i32).foo(); 
        &(simple_fn2 as fn(&mut i32)->i32).foo();
    
        strip(simple_fn).foo();
        strip2(simple_fn2).foo();
        strip3(simple_fn2).foo();
        strip4(simple_fn2).foo();
    }
  4. added
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    on Mar 9, 2017
  5. removed
    C-bugCategory: This is a bug.
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    on Jan 25, 2020
  6. oli-obk commented on Jan 25, 2020

    @oli-obk
    Contributor

    cc @rust-lang/wg-diagnostics when reporting that a trait is not implemented for a type, we should also search for impls that only differ in lifetimes and explain why/how the lifetimes are different

  7. added
    C-enhancementCategory: An issue proposing an enhancement or a PR with one.
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    and removed
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    on Jan 25, 2020
  8. added
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    D-confusingDiagnostics: Confusing error or lint that should be reworked.
    on Jan 25, 2020
  9. estebank commented on Feb 21, 2024

    @estebank
    Contributor

    Current output:

    error[E0599]: no method named `foo` found for fn pointer `fn(&mut i32) -> i32` in the current scope
      --> f103.rs:33:23
       |
    33 |     strip(simple_fn2).foo();
       |                       ^^^ method not found in `fn(&mut i32) -> i32`
       |
       = help: items from traits can only be used if the trait is implemented and in scope
    note: `FooFn` defines an item `foo`, perhaps you need to implement it
      --> f103.rs:1:1
       |
    1  | trait FooFn {
       | ^^^^^^^^^^^
    

    We still don't do what oli suggested, and we should.

  10. estebank commented on Nov 11, 2025

    @estebank
    Contributor

    We already currently have logic to check for alternative impls:

    fn suggest_traits_to_import(
    &self,
    err: &mut Diag<'_>,
    span: Span,
    rcvr_ty: Ty<'tcx>,
    item_name: Ident,
    inputs_len: Option<usize>,
    source: SelfSource<'tcx>,
    valid_out_of_scope_traits: Vec<DefId>,
    static_candidates: &[CandidateSource],
    unsatisfied_bounds: bool,
    return_type: Option<Ty<'tcx>>,
    trait_missing_method: bool,
    ) {
    let mut alt_rcvr_sugg = false;
    let mut trait_in_other_version_found = false;
    if let (SelfSource::MethodCall(rcvr), false) = (source, unsatisfied_bounds) {
    debug!(
    "suggest_traits_to_import: span={:?}, item_name={:?}, rcvr_ty={:?}, rcvr={:?}",
    span, item_name, rcvr_ty, rcvr
    );
    let skippable = [
    self.tcx.lang_items().clone_trait(),
    self.tcx.lang_items().deref_trait(),
    self.tcx.lang_items().deref_mut_trait(),
    self.tcx.lang_items().drop_trait(),
    self.tcx.get_diagnostic_item(sym::AsRef),
    ];
    // Try alternative arbitrary self types that could fulfill this call.
    // FIXME: probe for all types that *could* be arbitrary self-types, not
    // just this list.
    for (rcvr_ty, post, pin_call) in &[
    (rcvr_ty, "", None),
    (
    Ty::new_mut_ref(self.tcx, self.tcx.lifetimes.re_erased, rcvr_ty),
    "&mut ",
    Some("as_mut"),
    ),
    (
    Ty::new_imm_ref(self.tcx, self.tcx.lifetimes.re_erased, rcvr_ty),
    "&",
    Some("as_ref"),
    ),
    ] {
    match self.lookup_probe_for_diagnostic(
    item_name,
    *rcvr_ty,
    rcvr,
    ProbeScope::AllTraits,
    return_type,
    ) {

    that we could expand to account for alternative fn signatures.

  11. oli-obk commented on Nov 13, 2025

    @oli-obk
    Contributor

    can also try to use selection to ask the trait solver to find all impls that match if all lifetimes are replaced by infer vars

    Something I did with

            if let Ok(Some(ImplSource::UserDefined(impl_data))) =
                SelectionContext::new(self).select(&obligation.with(self.tcx, trait_ref.skip_binder()))
    

    in #148641

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-diagnosticsArea: Messages for errors, warnings, and lintsA-trait-systemArea: Trait systemC-enhancementCategory: An issue proposing an enhancement or a PR with one.D-confusingDiagnostics: Confusing error or lint that should be reworked.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions