Skip to content
New issue

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

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

Already on GitHub? # 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

# for free to join this conversation on GitHub. Already have an account? # 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