Skip to content

Tracking issue for const fn pointers #63997

Description

@gnzlbg

Sub-tracking issue for #57563.

This tracks const fn types and calling fn types in const fn.


From the RFC (https://github.com/oli-obk/rfcs/blob/const_generic_const_fn_bounds/text/0000-const-generic-const-fn-bounds.md#const-function-pointers):

const function pointers

const fn foo(f: fn() -> i32) -> i32 {
    f()
}

is illegal before and with this RFC. While we can change the language to allow this feature, two
questions make themselves known:

  1. fn pointers in constants

    const F: fn() -> i32 = ...;

    is already legal in Rust today, even though the F doesn't need to be a const function.

  2. Opt out bounds might seem unintuitive?

    const fn foo(f: ?const fn() -> i32) -> i32 {
        // not allowed to call `f` here, because we can't guarantee that it points to a `const fn`
    }
    const fn foo(f: fn() -> i32) -> i32 {
        f()
    }

Alternatively one can prefix function pointers to const functions with const:

const fn foo(f: const fn() -> i32) -> i32 {
    f()
}
const fn bar(f: fn() -> i32) -> i32 {
    f() // ERROR
}

This opens up the curious situation of const function pointers in non-const functions:

fn foo(f: const fn() -> i32) -> i32 {
    f()
}

Which is useless except for ensuring some sense of "purity" of the function pointer ensuring that
subsequent calls will only modify global state if passed in via arguments.

Activity

  1. added
    B-RFC-approvedBlocker: Approved by a merged RFC but not yet implemented.
    C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFC
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    T-langRelevant to the language team
    on Aug 29, 2019
  2. gnzlbg commented on Aug 29, 2019

    @gnzlbg
    ContributorAuthor

    I think that, at the very least, this should work:

    const fn foo() {}
    const FOO: const fn() = foo;
    const fn bar() { FOO() }
    const fn baz(x: const fn()) { x() }
    const fn bazz() { baz(FOO) }

    For this to work:

    • const must be part of fn types (just like unsafe, the extern "ABI", etc.)
    • we should allow calling const fn types from const fn

    Currently, const fns already coerce to fns, so const fn types should too:

    const fn foo() {}
    let x: const fn() = foo;
    let y: fn() = x; // OK: const fn => fn coercion

    I don't see any problems with supporting this. The RFC mentions some issues, but I don't see anything against just supporting this restricted subset.

    This subset would be super useful. For example, you could do:

    struct Foo<T>(T);
    trait Bar { const F: const fn(Self) -> Self; }
    
    impl<T: Bar> Foo<T> {
        const fn new(x: T) -> Self { Foo(<T as Bar>::F(x)) }
    }
    
    const fn map_i32(x: i32) -> i32 { x * 2 }
    impl Bar for i32 { const F: const fn(Self) -> Self = map_i32; } 
    const fn map_u32(x: i32) -> i32 { x * 3 }
    impl Bar for u32 { const F: const fn(Self) -> Self = map_u32; } 

    which is a quite awesome work around for the lack of const trait methods, but much simpler since dynamic dispatch isn't an issue, as opposed to:

    trait Bar { const fn map(self) -> Self; } 
    impl Bar for i32 { ... }
    impl Bar for u32 { ... }
    // or const impl Bar for i32 { ... {

    This is also a way to avoid having to use if/match etc. in const fns, since you can create a trait with a const, and just dispatch on it to achieve "conditional" control-flow at least at compile-time.

  3. RalfJung commented on Aug 29, 2019

    @RalfJung
    Member

    AFAIK const fn types are not even RFC'd, isn't it too early for a tracking issue?

  4. gnzlbg commented on Aug 29, 2019

    @gnzlbg
    ContributorAuthor

    Don't know, @Centril suggested that I open one.

    I have no idea why const fn types aren't allowed. AFAICT, whether a function is const or not is part of its type, and the fact that const fn is rejected in a type is an implementation / original RFC oversight. If this isn't the case, what is the case?

    EDIT: If I call a non-const fn from a const fn, that code fails to type check, so for that to happen const must be part of a fn type.

  5. removed
    B-RFC-approvedBlocker: Approved by a merged RFC but not yet implemented.
    T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.
    on Aug 29, 2019
  6. Centril commented on Aug 29, 2019

    @Centril
    Contributor

    AFAIK const fn types are not even RFC'd, isn't it too early for a tracking issue?

    Lotsa things aren't RFCed with respect to const fn. I want these issues for targeted discussion so it doesn't happen on the meta issue.

  7. oli-obk commented on Aug 29, 2019

    @oli-obk
    Contributor

    If I call a non-const fn from a const fn, that code fails to type check, so for that to happen const must be part of a fn type.

    it is.

    you can do

    const fn f() {}
    let x = f;
    x();

    inside a constant. But this information is lost when casting to a function pointer. Function pointers just don't have the concept of of constness.

  8. RalfJung commented on Aug 29, 2019

    @RalfJung
    Member

    Figuring out constness in function pointers or dyn traits is a tricky questions, with a lot of prior discussion in the RFC and the pre-RFC.

  9. tema3210 commented on Sep 25, 2019

    @tema3210

    whats about?
    pub trait Reflector { fn Reflect(&mut self)-> (const fn(Cow<str>)->Option<Descriptor>); }

  10. 10 remaining items

  11. beepster4096 commented on Mar 12, 2021

    @beepster4096
    Contributor
    impl<T> Cell<T> {
        pub fn with<U>(&self, func: const fn(&mut T) -> U) -> U;
    }

    Wouldn't const fn pointers make this sound? A const fn can't access a static or a thread-local, which would make this unsound.

    Edit: a cell containing a reference to itself makes this unsound

  12. HindrikStegenga commented on May 2, 2021

    @HindrikStegenga

    It seems assignment is currently broken, complaining about casts, even though no such casts are actually performed. (A const function simply assigning a function ptr basically). #83033

  13. RalfJung commented on May 3, 2021

    @RalfJung
    Member

    See my response at #83033 (comment) -- short summary: there is in fact a cast going on here; see the reference on "function item types" for more details.

  14. ketsuban commented on Dec 4, 2021

    @ketsuban
    Contributor

    Just in case other people run into this being unstable: It's still possible to use function pointers in const fn as long as they're wrapped in some other type (eg. a #[repr(transparent)] newtype or an Option<fn()>):

    I've found a case where this isn't true. (playground link)

    struct Handlers([Option<fn()>; _]);
    
    impl Handlers {
        const fn new() -> Self {
            Self([None; _])
        }
    }
  15. oli-obk commented on Dec 7, 2021

    @oli-obk
    Contributor

    Yea, just like with dyn Trait or generic trait bounds, there are workarounds to our checks and I'm convinced now we should just allow all of these like we do in const items. You can't call them, but you can pass them around.

  16. added a commit that references this issue on Mar 7, 2022
  17. added
    S-tracking-needs-summaryStatus: It's hard to tell what's been done and what hasn't! Someone should do some investigation.
    on Jun 22, 2022
  18. est31 commented on Mar 16, 2023

    @est31
    Member

    For const closures a lot has happened in 2022.

    This now works since rustc 1.61.0-nightly (03918badd 2022-03-07) (commit range is 38a0b81...03918ba but I can't narrow it down to a single PR... maybe a side effect of #93827 ???):

    #![feature(const_trait_impl)]
    
    const fn foo<T: ~const Fn() -> i32>(f: &T) -> i32 {
        f()
    }

    And since rustc 1.68.0-nightly (9c07efe84 2022-12-16) (I suppose the PR was #105725) you can even use impl Trait syntax:

    #![feature(const_trait_impl)]
    
    const fn foo(f: &impl ~const Fn() -> i32) -> i32 {
        f()
    }

    For const fn pointers, nothing much has happened though. This still errors:

    const fn foo(f: ~const fn() -> i32) -> i32 {
        f()
    }

    gives

    Details
    error: expected identifier, found keyword `fn`
     --> src/lib.rs:2:24
      |
    2 | const fn foo(f: ~const fn() -> i32) -> i32 {
      |                        ^^
      |
    help: use `Fn` to refer to the trait
      |
    2 | const fn foo(f: ~const Fn() -> i32) -> i32 {
      |                        ~~
    
    error: `~const` is not allowed here
     --> src/lib.rs:2:17
      |
    2 | const fn foo(f: ~const fn() -> i32) -> i32 {
      |                 ^^^^^^^^^^^^^^^^^^
      |
      = note: trait objects cannot have `~const` trait bounds
    
    error[E0782]: trait objects must include the `dyn` keyword
     --> src/lib.rs:2:17
      |
    2 | const fn foo(f: ~const fn() -> i32) -> i32 {
      |                 ^^^^^^^^^^^^^^^^^^
      |
    help: add `dyn` keyword before this trait
      |
    2 | const fn foo(f: dyn ~const fn() -> i32) -> i32 {
      |                 +++
    
    For more information about this error, try `rustc --explain E0782`.
    error: could not compile `playground` (lib) due to 3 previous errors
    

    Edit: note that while what I said is great progress, the two aren't well comparable, as the Fn trait support is for monomorphized generics cases, as in those where we know the type at const eval time and can derive the function from the type. dyn Fn trait support is still not existent. This gives a bunch of errors:

    #![feature(const_trait_impl)]
    const fn foo(f: &dyn ~const Fn() -> i32) -> i32 {
        f()
    }
  19. onlycs commented on Dec 29, 2023

    @onlycs
    Contributor

    Seems to have been patched. You need #[const_trait] attribute to use ~const, which the Fn trait doesn't have.
    rust-std says that it has "effects"

    ...
    #[must_use = "closures are lazy and do nothing unless called"]
    // FIXME(effects) #[const_trait]
    pub trait Fn<Args: Tuple>: FnMut<Args> {
    ...
  20. added
    A-const-evalArea: Constant evaluation, covers all const contexts (static, const fn, ...)
    and removed on Dec 1, 2024
  21. added
    S-tracking-needs-design-proposalStatus: This needs a clear design proposal and then a meeting with the team.
    and removed
    S-tracking-needs-summaryStatus: It's hard to tell what's been done and what hasn't! Someone should do some investigation.
    on Nov 9, 2025
  22. added
    T-typesRelevant to the types team, which will review and decide on the PR/issue.
    on Feb 14, 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

    A-const-evalArea: Constant evaluation, covers all const contexts (static, const fn, ...)C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCPG-const-traitsProject group: Const traitsS-tracking-needs-design-proposalStatus: This needs a clear design proposal and then a meeting with the team.T-langRelevant to the language teamT-typesRelevant to the types 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