Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

type annotation of closure args breaks borrow checking #100002

Open
aliemjay opened this issue Jul 31, 2022 · 1 comment
Open

type annotation of closure args breaks borrow checking #100002

aliemjay opened this issue Jul 31, 2022 · 1 comment
Labels
A-closures Area: Closures (`|…| { … }`) A-lifetimes Area: Lifetimes / regions C-bug Category: This is a bug. T-types Relevant to the types team, which will review and decide on the PR/issue.

Comments

@aliemjay
Copy link
Member

This example compiles as expected:

fn main() {
    let mut list = vec![];
    let mut add_to_list = |name: _| {
        list.push(name);
    };
    
    let name = String::from("name1");
    add_to_list(&name);
}

but when a type annotation is added to the closure argument, it fails:

-    let mut add_to_list = |name: _| {
+    let mut add_to_list = |name: &str| {

Workaround

Sometimes type annotation is necessary when type inference fails. :

fn main() {
    let mut list = vec![];
    let mut add_to_list = |name: _| { //ERROR type annotations needed
        if !name.is_empty() {
            list.push(name);
        }
    };
    
    let name = String::from("name1");
    add_to_list(&name);
}

In this case you can use this ugly hack to annotate the type in the closure body:

     let mut add_to_list = |name: _| {
+        let name: &str = name; // hack: annotate the type here to avoid rustc bug #xxx
         if !name.is_empty() {

But why?

When rustc encounters such closure, It has to pick up one of these two types for the closure:

/// A closure that expects an argument of SOME specific lifetime, `'a`.
type FnSig1<'a> = dyn         FnMut(&'a str);

/// A closure that expects an argument of ANY lifetime.
/// Aka higher-ranked lifetime.
type FnSig2     = dyn for<'a> FnMut(&'a str);

We want the first one here but the compiler is not smart enough to infer this. Instead it follows a set of dumb rules1 that leads it to the second type, and then it fails when borrow-checking the closure for the same reason the following fails:

fn test<'a, 'b>(mut list: Vec<&'a str>, name: &'b str) {
    list.push(name);
}

There is a promising work on a-mir-fomality to make this inference smarter.

Footnotes

  1. simply if the lifetime appears in type annotation, it's inferred to be higher-ranked

@aliemjay
Copy link
Member Author

aliemjay commented Jul 31, 2022

I have written this be referenced when addressing the breakages of #98835. Because I found most breakages were users relying on #98589 as a workaround for this limitation with closures.

@rustbot label C-bug T-types A-closures A-lifetimes

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-closures Area: Closures (`|…| { … }`) A-lifetimes Area: Lifetimes / regions C-bug Category: This is a bug. T-types Relevant to the types team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

2 participants