Is the code unreasonable?

Hello, when compiles the code:

    fn fx<'a, T: 'a>(r: &mut &'a mut T) -> fn(&mut &'a mut T) {
        fn x<'b, T: 'b>(_: &mut &'b mut T) {}
        let f = x::<'a, T>;

        type PtrF<'c, T> = for<'k> fn(&'k mut &'c mut T);

        f(r);
        f
    }

I get the waring:

   |
14 |         fn x<'b, T: 'b>(_: &mut &'b mut T) {}
   |                            - the late bound lifetime parameter is introduced here
15 |         let f = x::<'a, T>;
   |                     ^^
   |
   = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
   = note: for more information, see issue #42868 <https://github.com/rust-lang/rust/issues/42868>

e.g. specifies x::<'a, T> will be forbidden in the future.
The issue #42868 explains why, but this one is different by 'a is not late bounded. Code taken from the issue type PtrF<'c, T> = for<'k> fn(&'k mut &'c mut T); proves this.

Currently the code is accepted, why this will be forbidden to make the capability limited?

You don't use PtrF so it doesn't prove anything in the code you pasted. Did you mean this?

    fn x<'b, T: 'b>(_: &mut &'b mut T) {}
    type PtrF<'c, T> = for<'k> fn(&'k mut &'c mut T);
    let f: PtrF<'a, T> = x;

It avoids the lint by avoiding the need to turbofish x (but you have to coerce to a function pointer).

You can just not define f and use x directly to avoid the lint without coercing to a function pointer.

You can continue to use f but let the lifetime be inferred and likewise avoid the function pointer.


'a is early-bound, but that doesn't make this example different. The lint is about using lifetimes in turbofish when the function signature has both late and early bound lifetime parameters. It's not about what lifetimes you use in the turbofish.

Here are the lifetimes in the signature of x:

    // Bound in a where-clause and thus early-bound
    //   vvvvvvvvv              vv
    fn x<'b, T: 'b>(_: &'_ mut &'b mut T) {}
    // late-bound       ^^

And then when you turbofish it:

let f = x::<'a, T>;
//          ^^

The problem is specifying the (early-bound) lifetime parameters of x at all, and it doesn't matter if the lifetime specified ('a) is late or early bound.


:person_shrugging: There's some movement around early and late lifetime parameters going on, but I don't have a good feel for what that movement is. I would find allowing this pattern and not being able to name implicit unconstrained late-bound lifetimes reasonable, personally.

I do find the compatibility lint wording misleading and unfortunate...

  = warning: this was previously accepted by the compiler but is
    being phased out; it will become a hard error in a future release!

...because I've ran into at least two cases where, when I looked it up, plans had changed and the current direction was to accept compiling code. [1] But the warning flat out says you can take it as a given that it will become an error.

But I have no idea what the type or compiler teams think about this particular issue.

If you have an example that's not so trivially solvable,[2] perhaps share it here and/or in the issue.


  1. E.g. this one, which even a long-term compiler dev took it literally. ↩︎

  2. that is, trivial to avoid the lint ↩︎

1 Like

you are right that 'a is not late bounded, the warning originated from x, because x has late bounded lifetime parameter (just elided), the post in #42868 has this paragraph:

if you look carefully at the warning message, it point exactly to the elided implicit lifetime parameter:

as suggested in #42868, you could simply remove the explicit lifetime argument, everything should work out as expected:

fn fx<'a, T: 'a>(r: &mut &'a mut T) -> fn(&mut &'a mut T) {
        fn x<'b, T: 'b>(_: &mut &'b mut T) {}
        type PtrF<'c, T> = for<'k> fn(&'k mut &'c mut T);

        if true {
            // just as your original code
            // it's ok to remove the lifetime argument
            let f = x::<T>;
            // it's unnecessary but, if you want to spell out the lifetime 'a, PtrF can be used:
            let f: Ptr<'a, T> = x;
            f(r);
            f
        } else {
            // or you can get rid of `f` entirely and just call x
            x(r);
            x
        }
    }

I can't remove it, because I need it statically part of the signature of the returned function, which in return should fail to compile if the passed argument with another lifetime instead of the specific lifetime.

more detail is at Why the code compiles with different lifetime?

I don't know what happens if I remove it. May it cause the lifetime to be late bounded? While, document says T:'a will force it to be early bounded....., and what is an early bounded lifetime if it is not specified in the compiling time

you already specified the lifetime bounds in the type of fx, spelling out the `a lifetime in your code is really meaningless. whatever you are trying to achieve, this is not the correct method. please describe your higher level goal and maybe people here can give more helpful suggestions.

2 Likes

I need return another function which is bounded with the lifetime too, which when gets called later, fails to compile if the provided argument does not contain the same lifetime.

The body of a function does not change the API guarantees of the function. The API guarantees are based on the signature of the function. [1]

So adding the turbofish inside fx cannot make a program that compiles without the turbofish fail to compile (or vice-versa). Calls to fx compile or don't compile based on the signature of fx only.


  1. The one exception is that auto traits of return position impl Trait leak through the API, but there is no impl Trait here. ↩︎

1 Like

you are repeating yourself. if you are misunderstanding your problem, you might be asking the wrong question.

there's no such thing of "a function only accept arguments containing the same lifetime", this is just not how rust lifetime works. the type checker ensures proper lifetime bounds, which are expressed as subtype relations, not in terms of exact lifetime argument.

people who want to help need to understand your problem first.

2 Likes

I understand finally, so I can delete the lifetime in the body, the fx's return-value signature assures what I want will be satisfied.

Thanks @quinedot @nerditation

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.