Skip to content

Tracking issue for Fn traits (unboxed_closures & fn_traits feature)Β #29625

Open

Description

@aturon

View all comments

Tracks stabilization for the Fn* traits.

Random bugs:

Activity

  1. added
    T-langRelevant to the language team
    B-unstableBlocker: Implemented in the nightly compiler and unstable.
    on Nov 5, 2015
  2. nagisa commented on May 13, 2016

    @nagisa
    Member

    Inability to delegate calls to other FnOnce implementors like this:

    struct A<T>(T);
    
    impl<T, Args> FnOnce<Args> for A<T>
    where T: FnOnce<Args> {
        type Output = <T as FnOnce<Args>>::Output;
        fn call_once(self, args: Args) -> Self::Output { FnOnce::call_once(self.0, args) }
    }

    is the reason I have a safety issue in libloading.

  3. petrochenkov commented on Jul 12, 2016

    @petrochenkov
    Contributor

    FnOnce::Output is stabilized in #34365

  4. nrc commented on Aug 17, 2016

    @nrc
    Member

    Could someone summarise what is blocking stabilisation here please?

  5. nagisa commented on Aug 18, 2016

    @nagisa
    Member

    @nrc uncertainty about Args being the right thing given the possibility of variadic generics coming along around 2030 is the most major reason this is unstable.

  6. aturon commented on Aug 27, 2016

    @aturon
    ContributorAuthor

    @nrc also possibly some questions around the inheritance relationship, see #19032

  7. added
    B-RFC-implementedBlocker: Approved by a merged RFC and implemented but not stabilized.
    on Aug 29, 2016
  8. added
    C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFC
    on Jul 22, 2017
  9. brunoczim commented on Jan 18, 2018

    @brunoczim

    There should be a way of casting fn(A) -> B to Fn(A) -> B

  10. SimonSapin commented on Jan 18, 2018

    @SimonSapin
    Contributor

    @brunoczim That coercion already happens implicitly, but Fn() as a type is a trait object so it needs to be behind some kind of pointer:

    use std::rc::Rc;
    fn main() {
        let _: &Fn() = &main;
        let _: Box<Fn()> = Box::new(main);
        let _: Rc<Fn()> = Rc::new(main);
    }
  11. Michael-F-Bryan commented on Apr 2, 2018

    @Michael-F-Bryan
    Contributor

    One issue we've been discussing on TheDan64/inkwell#5 is the ability to mark something as unsafe to use. What about having an UnsafeFn marker trait which could be used to tell the compiler a callable is unsafe to call?

    For context, inkwell is a wrapper around LLVM and we're trying to figure out how to return a function pointer to a JIT compiled function, when calling the function is fundamentally unsafe for the same reasons FFI code is unsafe to call.

  12. CodeSandwich commented on May 4, 2018

    @CodeSandwich
    Contributor

    There is another reason to add UnsafeFn: currently unsafe fns don't implement Fn. There is no way to pass unsafe fn as a function argument, they are second class citizen: example.

    UnsafeFn could be implemented for things implementing Fn, so Fns can be used wherever UnsafeFn is required. There probably also should be UnsafeFnMut and UnsafeFnOnce.

  13. alexreg commented on Aug 20, 2018

    @alexreg
    Contributor

    @Michael-F-Bryan @CodeSandwich This sounds like something for which an RFC would really be appreciated. It probably wouldn't be an overly long or intricate one to write, even. I would support it, for sure, and judging by an issue I saw about this not long ago (a long-standing one), others would too.

  14. 63 remaining items

  15. bjorn3 commented on Dec 19, 2025

    @bjorn3
    Member

    () is not a valid function name. And in any case we have three different traits. They need different method names to avoid ambiguity.

  16. EienMiku commented on Dec 19, 2025

    @EienMiku

    () is not a valid function name. And in any case we have three different traits. They need different method names to avoid ambiguity.

    It is indeed illegal. But if modify the compiler to allow it, this would look better than the current syntax to me. Moreover, though we have multiple Fn* traits, it seems that there might not be ambiguity, as it's unlikely that we would need to implement all of them for a struct.

  17. bjorn3 commented on Dec 19, 2025

    @bjorn3
    Member

    Implementing Fn requires implementing FnMut and FnOnce too given that they are supertraits of Fn. If a subtrait and a supertrait have the same method name, I believe rustc doesn't allow calling the method currently for ambiguity reasons.

  18. madsmtm commented on Dec 19, 2025

    @madsmtm
    Member

    Also, doing special syntax for builtin operations isn't something that is done elsewhere, for example the Add trait has a method called add, not +.

  19. mikem8891 commented on Jan 7, 2026

    @mikem8891

    Implementing Fn requires implementing FnMut and FnOnce too given that they are supertraits of Fn.

    FnOnce and FnMut are supertraits of Fn, but I don't think they should be. I agree that every Fn trait needs to implements FnMut and FnOnce, but Fn does not rely on anything special about how the other traits are implemented; therefore, they should not be prerequisites to implementing Fn. In fact, it would be really annoying if they were implemented and any different than the Fn implementation. Instead, only Fn should be implemented and a trivial blanket implementation should be used to implement FnMut and FnOnce, see the example below. The example is the Fn* traits using blanket implementations instead of supertraits.

    trait Fn<Args> {
        fn call(&self, args: Args);
    }
    
    trait FnMut<Args> {
        fn call_mut(&mut self, args: Args);
    }
    
    trait FnOnce<Args> {
        fn call_once(self, args: Args);
    }
    
    impl<T: Fn<Args>, Args> FnMut<Args> for T {
        fn call_mut(&mut self, args: Args) {
            self.call(args)
        }
    }
    
    impl<T: FnMut<Args>, Args> FnOnce<Args> for T {
        fn call_once(mut self, args: Args) {
            self.call_mut(args)
        }
    }
    

    play with it (https://play.rust-lang.org/?version=stable&mode=debug&edition=2024&gist=cb845122a8404c17bac1d04a6bdfb20d)

  20. bjorn3 commented on Jan 7, 2026

    @bjorn3
    Member

    I think that would be a breaking change by no longer allowing downcasting from dyn Fn() to dyn FnMut().

  21. fogti commented on Jan 8, 2026

    @fogti
    Contributor

    One could easy have both: There should be nothing that prevents one from requiring smth. as a supertrait, and still offering a blanket impl for the supertrait based upon the subtrait.

    Perhaps beware of afaik resolution gotchas like #141807

  22. Nekidev commented on Mar 10, 2026

    @Nekidev

    Is Fn(A, B) vs Fn<(A, B)> still a topic being discussed? I had quite some hard time getting in my head that Fn(A, B) is not Fn<A, B> rather Fn<(A, B)>.

  23. jeffs commented on Mar 10, 2026

    @jeffs
    Contributor

    @Nekidev Variadic generics aren't currently supported by the language, for a number of subtle reasons. See also rust-lang/lang-team#348.

  24. peter-lyons-kehl commented on Jun 15, 2026

    @peter-lyons-kehl
    Contributor

    Timeline for variadic generics: Based on how long const generics, and generic associated types, took, is it not likely that this would take many years? Plus complications/interactions that we don't know about yet (with the new trait solver, or anything else new in the compiler).

    EDIT: Sorry, the below doesn't work (that is, it does compile with two impl that we want to conflict). I'll investigate more.

    EDIT again: See the following comment for an example.

    but that wouldn't scale to function-like objects with generic arguments, for which an associated type wouldn't work.

    @fogti Do you mean when Args depends on any generic argument(s) of the object for which Fn* is implemented? Solution is below.

    I don't know how to support higher-order signatures while at the same time preventing multiple call signatures for a type other than keeping Fn* perma-unstable.

    @bjorn3 (If I understand right, and as mentioned by others earlier), we can prevent multiple call signatures by changing Args to be an associative type. BUT, this associative type doesn't have to be in Fn* traits (as we know them) - it could be in a parent trait(s), whose only purpose is to prevent the multiple call signatures. I believe the following should continue to work for Higher-Ranked Trait Bounds. So all current internal/nightly code could continue to work.

    EDIT: Sorry, the below doesn't work (that is, it does compile with two impl that we want to conflict). I'll investigate more.

    EDIT again: See the following comment for an example.

    // This example defines `FnOnce` only, and it omits `call_once`.
    
    // The following two lines are here just for minimizing the `unstable` surface of this example, and it allows you to compile it with `stable` Rust toolchain.
    pub trait Tuple {}
    impl<T> Tuple for (T,) {}
    
    // Internal. Not sure if we need FnOnceAssociated to be sealed more.
    #[allow(dead_code)]
    trait FnOnceAssociated<A: Tuple> {
        type Args: Tuple;
    }
    
    pub trait FnOnce<A: Tuple> : FnOnceAssociated<A> {
        type Output;
    }
    // Blanket impl ensures that users/3rd parties can't implement it for FnOnce (and friends).
    /*impl<F: FnOnce<A>, A: Tuple> FnOnceAssociated<A> for F {
        type Args = A;
    }*/
    // OR, even a total blanket impl - unsure if useful, and possibly confusing:
     impl<F, A: Tuple> FnOnceAssociated<A> for F {
        type Args = A;
    }
    //-------
    
    // Usage:
    pub struct GenericStruct<T> {
        _t: T,
    }
    impl<T> FnOnce<(bool,)> for GenericStruct<T> {
        type Output = bool;
    }
    
    // I've hoped for the following fails to compile, conflicting with the previous impl - but it doesn't...
    impl<T> FnOnce<(u8,)> for GenericStruct<T> {
        type Output = bool;
    }

    Side note: I'm surprised that the first blanket impl (commented out) in the above example actually compiles. Is it likely to continue to compile with the next trait solver?

  25. peter-lyons-kehl commented on Jun 15, 2026

    @peter-lyons-kehl
    Contributor

    The following prevents multiple signatures/multiple impl of FnOnce/FnMut/Fn for the same type, while keeping Args as a generic parameter, hence supporting higher rank trait bounds.

    This adds an associative type Args, but not in FnOnce/FnMut/Fn traits, because their generic instantiations are separate and having an associative type in them would not prevent multiple signatures/implementations. So the associative type needs a new trait (a parent trait, or a side kick trait; called FnBase, or FnPreventMultipleSignatures or similar).

    It's not ideal, because you need to implement one more trait. But we already have three traits, and this new one is tiny.

    That does involve duplication of Args

    • as a generic param to FnOnce/FnMut/Fn, and
    • as an associated type in FnBase.
    // Out of three existing `Fn*` traits, this example defines `FnOnce` only, and it omits `call_once`.
    
    // The following two lines are here just for minimizing the `unstable` surface of this example, and it allows you to compile it with `stable` Rust toolchain.
    pub trait Tuple {}
    impl<T> Tuple for (T,) {}
    impl<T, U> Tuple for (T,U) {}
    // ...
    
    pub trait FnBase /* or: FnPreventMultipleSignatures or similar */ {
        type Args: Tuple;
    }
    
    pub trait FnOnce<A: Tuple> : FnBase<Args = A>
    // Or, instead of trait inheritance, have a side kick trait and a bound on Self:
    //
    //where Self: FnBase< Args = A>
    {
        type Output;
    }
    
    // Usage:
    pub struct GenericStruct<T> {
        _t: T,
    }
    impl<T> FnBase for GenericStruct<T> {
        type Args = (bool,);
    }
    impl<T> FnOnce<(bool,)> for GenericStruct<T> {
        type Output = ();
    }
    
    // The following fails to compile - GOOD.
    /*impl<T> FnOnce<(u8,T)> for GenericStruct<T> {
        type Output = ();
    }*/

    Unfortunately, I don't see a way to have blanket impl for FnBase for any type that implements FnOnce.

  26. Nadrieril commented on Jun 15, 2026

    @Nadrieril
    Member

    Hi all, tracking issues are normally reserved for updates about the development of a feature. To discuss the feature and/or propose extensions, the appropriate venues are the internals forum, Zulip, the RFC PR if it's still open (which isn't the case here), or writing your own RFC.

    If you open a relevant Zulip/Internals topic, do post it here so that interested people can come and discuss with you over there.

  27. peter-lyons-kehl commented on Jun 16, 2026

    @peter-lyons-kehl
    Contributor
  28. added
    T-libsRelevant to the library team, which will review and decide on the PR/issue.
    and removed
    T-libs-api[DEPRECATED; DO NOT USE]
    on Aug 12, 2026
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

    B-RFC-implementedBlocker: Approved by a merged RFC and implemented but not stabilized.B-unstableBlocker: Implemented in the nightly compiler and unstable.C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCF-unboxed_closures`#![feature(unboxed_closures)]`S-tracking-design-concernsStatus: There are blocking design concerns.T-langRelevant to the language teamT-libsRelevant to the library 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