Skip to content

Instantly share code, notes, and snippets.

@nikomatsakis
Last active April 20, 2016 10:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nikomatsakis/631ec8b4af9a18b5d062d9d9b7d3d967 to your computer and use it in GitHub Desktop.
Save nikomatsakis/631ec8b4af9a18b5d062d9d9b7d3d967 to your computer and use it in GitHub Desktop.
`hr_lifetime_in_assoc_type` future-compatibility warning

This is the summary issue for the hr_lifetime_in_assoc_type future-compatibility warning and other related errors. The goal of this page is describe why this change was made and how you can fix code that is affected by it. It also provides a place to ask questions or register a complaint if you feel the change should not be made. For more information on the policy around future-compatibility warnings, see our [breaking change policy guidelines][].

What is the warning for?

The warning is issued in two cases. First, if you have a where-clause where a higher-ranked lifetime appears only in the associated type. This most commonly occurs when the lifetime appears in the return type for one of the closure traits:

where F: for<'a> Fn() -> &'a u32
//                        ^^ this one

But could also occur in other associated type bindings:

where T: for<'a> Iterator<Item=&'a u32>
//                              ^^ this one

The second situation is when you have a fn type with a lifetime that appears only in the return type:

let x: for<'a> fn() -> &'a i32
//                      ^^ this one

Note that it is perfectly fine for a higher-ranked lifetime to appear in an associated type or function return type if it also appears in the trait inputs or function arguments:

where F: for<'a> Fn(&'a u32) -> &'a u32 // OK!
//                   ^^ appears in trait input types here

where for<'a> &'a T: Iterator<Item=&'a u32> // OK!
//             ^^ appears in trait input types here

let x: for<'a> fn(&'a i32) -> &'a i32
//                 ^^ appears in function arguments here

These constructs are being rejected as part of the fix for issue rust-lang/rust#32330. This is a soundness bug that concerns cases like these. In general, all higher-ranked lifetimes that are used in the value of an associated type (and in this case, the function return type is an associated type) must also appear in the trait's input types. This constraint is already enforced for impls, but enforcement was overlooked for where-clauses.

These sorts of where-clauses and types are not only ill-formed, they are useless. For example, now that #32330 is fixed, there are no types that could satisfy such a where-clause, nor are there any functions with a type like for<'a> fn() -> &'a i32 (see next section for more details).

Other errors

As part of the fix for #32330, we also changed how lifetime parameters on fn declarations are handled. It used to be that a lifetime parameter was considered higher-ranked (also called "late-bound") so long as it did not appear in a where-clause. The new rule is that the parameter was also appear in the fn argument types. This change is usually harmless, but it can trigger compilation errors of various kinds. For engineering reasons, it was not possible to issue warnings for those errors, but in some cases we can identify after the fact that the error was a result of this change and hence you may have been directed to this issue in such a case.

Here is an example of an affected function:

fn foo<'a>() -> &'a i32 { }
//               ^^ no longer higher-ranked

Merely calling this function should work as before. But assigning it to a fn pointer variable may now trigger an error, depending on the type of that variable:

fn main() {
    let f: for<'a> fn() -> &'a i32 = foo;
    //                      ^^ note that we will now issue a warning for this type, as well.
}

Another, more subtle, error can occur when a function is stored into a variable and then invoked multiple times:

fn bar<'b, 'c>() -> (&'b i32, 'c i32) {
    let f = foo;
    (f(), f()) // <-- error
    // but note that (foo(), foo()) works
}

Essentially, the change is that each time you reference foo (directly!), there is now a single lifetime assigned for its parameter 'a, no matter how many times you call it. In cases like bar above, we need to assign distinct references to each call, so we have to reference foo twice.

When will this warning become a hard error?

At the beginning of each 6-week release cycle, the Rust compiler team will review the set of outstanding future compatibility warnings and nominate some of them for Final Comment Period. Toward the end of the cycle, we will review any comments and make a final determination whether to convert the warning into a hard error or remove it entirely.

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