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

implied lifetimes lead to mismatched type error with seemingly identical types in the msg - async closure edition #108114

Open
luketpeterson opened this issue Feb 16, 2023 · 3 comments
Labels
A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@luketpeterson
Copy link

Code

use core::future::{Future};
use anyhow::Error;

struct Session {}

impl Session {
    pub async fn dispatch_request<T, F, Fut>(&self, interpreter: F) -> Result<T, Error>
        where
        F: FnMut(&mut String) -> Fut,
        Fut: Future<Output = Result<T, Error>>,
    {
        let mut interpreter = interpreter;
        let mut stream = "hello".to_string();
        interpreter(&mut stream).await
    }
}

async fn test_interpreter(input_stream: &mut String) -> Result<String, Error> {

    Ok(input_stream.clone())
}

#[tokio::main]
async fn main() {
    let session = Session{};

    session.dispatch_request(test_interpreter).await.unwrap();
}

Current output

Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
  --> src/main.rs:27:5
   |
27 |     session.dispatch_request(test_interpreter).await.unwrap();
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected trait `for<'a> <for<'a> fn(&'a mut String) -> impl Future<Output = Result<String, anyhow::Error>> {test_interpreter} as FnOnce<(&'a mut String,)>>`
              found trait `for<'a> <for<'a> fn(&'a mut String) -> impl Future<Output = Result<String, anyhow::Error>> {test_interpreter} as FnOnce<(&'a mut String,)>>`
note: the lifetime requirement is introduced here
  --> src/main.rs:9:34
   |
9  |         F: FnMut(&mut String) -> Fut,
   |                                  ^^^

For more information about this error, try `rustc --explain E0308`.
error: could not compile `playground` due to previous error

Desired output

Something that makes it more clear what is causing the problem or how to address it.

Rationale and extra context

This thread has some additional context as well as two proposed solutions. https://users.rust-lang.org/t/lifetime-bounds-to-use-for-future-that-isnt-supposed-to-outlive-calling-scope/89277

I understand this situation is one among a set of situations under which the same confusing error might manifest. However this might be a candidate for a special case check.

Thank you.

Other cases

No response

Anything else?

No response

@luketpeterson luketpeterson added A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue. labels Feb 16, 2023
@ariesdevil
Copy link

Please try:

use core::future::{Future};
use anyhow::Error;

struct Session {}

impl Session {
    pub async fn dispatch_request<'a, T, F, Fut>(&self, interpreter: F) -> Result<T, Error>
        where
        F: FnMut(&'a mut String) -> Fut, // <-- note the lifetime annotation
        Fut: Future<Output = Result<T, Error>>,
    {
        let mut interpreter = interpreter;
        let mut stream = "hello".to_string();
        interpreter(&mut stream).await
    }
}

async fn test_interpreter(input_stream: &mut String) -> Result<String, Error> {

    Ok(input_stream.clone())
}

#[tokio::main]
async fn main() {
    let session = Session{};

    session.dispatch_request(test_interpreter).await.unwrap();
}

@luketpeterson
Copy link
Author

With the explicit lifetime annotation, the error is sensible.

I filed this issue to see if there was a way to make that the default error message with elided lifetimes as well. Thank you.

@QuineDot
Copy link

QuineDot commented Mar 1, 2023

Incidentally, as someone who understands that making the lifetime a function parameter means it's too long to borrow a local for, and thus understands that I need a HRTB, the alternative version is something I'm very unlikely to try. Additionally, while the error when you do so is a reasonable message for what it talks about, it's an error about wrongfully making the lifetime a function parameter; that function can't compile at all.

What is desired here is an error explaining why the function bounds aren't general enough for closures which produce futures that capture input lifetimes. The function in the OP compiles just fine, the problem comes when you attempt to call it with closures too general for the bound. Probably the function writer intended the more general bound (but there's no good way to write it).

Making the HRTB more explicit doesn't produce a better error.

use core::future::{Future};
use anyhow::Error;

struct Session {}

impl Session {
    pub async fn dispatch_request<T, F, Fut>(&self, interpreter: F) -> Result<T, Error>
        where
        F: for<'a> FnMut(&'a mut String) -> Fut,
        Fut: Future<Output = Result<T, Error>>,
    {
        let mut interpreter = interpreter;
        let mut stream = "hello".to_string();
        interpreter(&mut stream).await
    }
}

async fn test_interpreter(input_stream: &mut String) -> Result<String, Error> {

    Ok(input_stream.clone())
}

#[tokio::main]
async fn main() {
    let session = Session{};

    session.dispatch_request(test_interpreter).await.unwrap();
}
error[[E0308]](https://doc.rust-lang.org/stable/error_codes/E0308.html): mismatched types
  --> src/main.rs:27:5
   |
27 |     session.dispatch_request(test_interpreter).await.unwrap();
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected trait `for<'a> <for<'a> fn(&'a mut String) -> impl Future<Output = Result<String, anyhow::Error>> {test_interpreter} as FnOnce<(&'a mut String,)>>`
              found trait `for<'a> <for<'a> fn(&'a mut String) -> impl Future<Output = Result<String, anyhow::Error>> {test_interpreter} as FnOnce<(&'a mut String,)>>`
note: the lifetime requirement is introduced here
  --> src/main.rs:9:45
   |
9  |         F: for<'a> FnMut(&'a mut String) -> Fut,
   |                                             ^^^

Making the HRTB more explicit with a name other than 'a also does not, but produces a different error, which is odd. Is 'a special cased or something? This version doesn't show the cast from the function item type.

use core::future::{Future};
use anyhow::Error;

struct Session {}

impl Session {
    pub async fn dispatch_request<T, F, Fut>(&self, interpreter: F) -> Result<T, Error>
        where
        F: for<'b> FnMut(&'b mut String) -> Fut,
        Fut: Future<Output = Result<T, Error>>,
    {
        let mut interpreter = interpreter;
        let mut stream = "hello".to_string();
        interpreter(&mut stream).await
    }
}

async fn test_interpreter(input_stream: &mut String) -> Result<String, Error> {

    Ok(input_stream.clone())
}

#[tokio::main]
async fn main() {
    let session = Session{};

    session.dispatch_request(test_interpreter).await.unwrap();
}
error[[E0308]](https://doc.rust-lang.org/stable/error_codes/E0308.html): mismatched types
  --> src/main.rs:27:5
   |
27 |     session.dispatch_request(test_interpreter).await.unwrap();
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected trait `for<'b> FnOnce<(&'b mut String,)>`
              found trait `for<'a> FnOnce<(&'a mut String,)>`
note: the lifetime requirement is introduced here
  --> src/main.rs:9:45
   |
9  |         F: for<'b> FnMut(&'b mut String) -> Fut,
   |                                             ^^^

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-diagnostics Area: Messages for errors, warnings, and lints T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.
Projects
None yet
Development

No branches or pull requests

3 participants