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

Unclear "higher-ranked lifetime error" error #115525

Open
DCNick3 opened this issue Sep 4, 2023 · 3 comments
Open

Unclear "higher-ranked lifetime error" error #115525

DCNick3 opened this issue Sep 4, 2023 · 3 comments
Labels
A-diagnostics Area: Messages for errors, warnings, and lints A-higher-ranked Area: Higher-ranked things (e.g., lifetimes, types, trait bounds aka HRTBs) A-lifetimes Area: Lifetimes / regions D-terse Diagnostics: An error or lint that doesn't give enough information about the problem at hand. E-needs-mcve Call for participation: This issue has a repro, but needs a Minimal Complete and Verifiable Example T-compiler Relevant to the compiler team, which will review and decide on the PR/issue.

Comments

@DCNick3
Copy link

DCNick3 commented Sep 4, 2023

Code

main.rs:

use tokio::io::{self, AsyncWriteExt};

pub async fn get_by_id<I, S>(ids: I) -> io::Result<()>
where
    I: IntoIterator<Item = S>,
    S: Into<String>,
{
    for s in ids {
        let s = s.into();
        let mut stdout = io::stdout();
        stdout.write_all(s.as_bytes()).await?;
    }
    
    Ok(())
}

async fn beba() -> io::Result<()> {
    let audios = vec![(1, 2)];
    let _audios = get_by_id(
        audios
            .iter()
            .map(|(a, b)| format!("{}_{}", a, b)),
    )
    .await?;

    Ok(())
}

trait FnTrait {}

impl<F, Fut> FnTrait for F
where
    F: Fn() -> Fut + Send + Sync + 'static,
    Fut: std::future::Future<Output = io::Result<()>> + Send + 'static,
{}

fn check_fn<F: FnTrait>(_f: F) {}

#[tokio::main]
async fn main() -> io::Result<()> {
    check_fn(beba);

    Ok(())
}

Cargo.toml:

[package]
name = "hrle-repro"
version = "0.1.0"
edition = "2021"

[dependencies]
tokio = { version = "1.32.0", features = ["full"] }

Current output

error: higher-ranked lifetime error
  --> src/main.rs:41:5
   |
41 |     check_fn(beba);
   |     ^^^^^^^^^^^^^^
   |
   = note: could not prove `fn() -> impl Future<Output = Result<(), std::io::Error>> {beba}: FnTrait`

error: could not compile `hrle-repro` (bin "hrle-repro") due to previous error

Desired output

I am not sure of the exact wording, but it should explain that the failure is due to the Future returned from the function not implementing Send and Sync

Rationale and extra context

Without explanation of why does the passed function not implement the required trait, it is not immediately clear what the issue is.

Other cases

The error output is the same on stable and nightly

Anything else?

Originally minimized from this code using reqwest and teloxide

@DCNick3 DCNick3 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 Sep 4, 2023
@rustbot rustbot added the needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. label Sep 4, 2023
@fmease fmease added A-lifetimes Area: Lifetimes / regions E-needs-mcve Call for participation: This issue has a repro, but needs a Minimal Complete and Verifiable Example D-terse Diagnostics: An error or lint that doesn't give enough information about the problem at hand. and removed needs-triage This issue may need triage. Remove it if it has been sufficiently triaged. labels Sep 6, 2023
@zjp-CN
Copy link

zjp-CN commented Jan 10, 2024

The error now becomes

error: implementation of `FnOnce` is not general enough
  --> src/main.rs:41:5
   |
41 |     check_fn(beba);
   |     ^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
   |
   = note: closure with signature `fn(&'0 (i32, i32)) -> String` must implement `FnOnce<(&'1 (i32, i32),)>`, for any two lifetimes `'0` and `'1`...
   = note: ...but it actually implements `FnOnce<(&(i32, i32),)>`

error: implementation of `Iterator` is not general enough
  --> src/main.rs:41:5
   |
41 |     check_fn(beba);
   |     ^^^^^^^^^^^^^^ implementation of `Iterator` is not general enough
   |
   = note: `Iterator` would have to be implemented for the type `std::slice::Iter<'0, (i32, i32)>`, for any lifetime `'0`...
   = note: ...but `Iterator` is actually implemented for the type `std::slice::Iter<'1, (i32, i32)>`, for some specific lifetime `'1`

It's still not clear what happened. But I'll mimimize as follows which emits the same errors implementation of `FnOnce`/`Iterator` is not general enough.

use std::future::Future;

pub async fn get_by_id<I>(ids: I)
where
    I: IntoIterator<Item = String>,
{
    for s in ids {
        async { s.as_bytes() }.await;
    }
}

async fn beba() {
    get_by_id([1].iter().map(|_| String::new())).await;
}

trait FnTrait {}

impl<F, Fut> FnTrait for F
where
    F: Fn() -> Fut + Send + Sync + 'static,
    Fut: Future<Output = ()> + Send + 'static,
{
}

fn check_fn<F: FnTrait>(_f: F) {}

fn main() {
    check_fn(beba);
}

To make the code above compile (in some sense), you can do one of these

  • remove Send bound on Fut

  • pass an owned iterator: [1].into_iter()

  • manually desugar async fn as impl Future, but with with some twists and turns: I'll go along with this

    pub fn get_by_id<I>(ids: I) -> impl Future<Output = ()>
    where
        I: IntoIterator<Item = String>,
    {
        async move {
            for s in ids {
                async { s.as_bytes() }.await;
            }
        }
    }
    // same errors: implementation of `FnOnce`/`Iterator` is not general enough

    first try: -> impl Future<Output = ()>

    pub fn get_by_id<I>(ids: I) -> impl Send + Future<Output = ()> { ... }
    
    error: future cannot be sent between threads safely
     --> src/main.rs:3:32
      |
    3 | pub fn get_by_id<I>(ids: I) -> impl Send + Future<Output = ()>
      |                                ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ future created by async block is not `Send`
      |
    note: captured value is not `Send`
     --> src/main.rs:8:18
      |
    8 |         for s in ids {
      |                  ^^^ has type `I` which is not `Send`
    help: consider further restricting this bound
      |
    5 |     I: IntoIterator<Item = String> + std::marker::Send,
      |                                    +++++++++++++++++++

    second try: -> impl Send + Future<Output = ()>, cool, the error becomes friendlier and tells us what to do

    // success
    pub fn get_by_id<I>(ids: I) -> impl Send + Future<Output = ()>
    where
        I: Send + IntoIterator<Item = String>, // the compiler suggests
        <I as IntoIterator>::IntoIter: Send // also the compiler suggests

    third try (or maybe forth if you add one of the two bounds at a time): success.

For OP in question, by applying the third way, it compiles.

@jorendorff
Copy link
Contributor

jorendorff commented Jan 25, 2024

I have a similar case, where a mysterious error, triggered while the compiler is trying to prove something is Send, is presented without that context.

This one doesn't have any dependencies. Same behavior in Stable and Nightly.

   Compiling playground v0.0.1 (/playground)
error: implementation of `From` is not general enough
  --> src/lib.rs:67:5
   |
67 |     spawn(handle_connection());
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `From` is not general enough
   |
   = note: `Box<(dyn std::error::Error + Send + Sync + 'static)>` must implement `From<Box<(dyn std::error::Error + Send + Sync + '0)>>`, for any lifetime `'0`...
   = note: ...but it actually implements `From<Box<(dyn std::error::Error + Send + Sync + 'static)>>`

error: could not compile `playground` (lib) due to 1 previous error

This one can be solved by refactoring the body of handle_connection a little. But the issue is the error message.

forum post about this one

@jorendorff
Copy link
Contributor

...Mine at least is probably a duplicate of #102211.

@fmease fmease added the A-higher-ranked Area: Higher-ranked things (e.g., lifetimes, types, trait bounds aka HRTBs) label Sep 24, 2024
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 A-higher-ranked Area: Higher-ranked things (e.g., lifetimes, types, trait bounds aka HRTBs) A-lifetimes Area: Lifetimes / regions D-terse Diagnostics: An error or lint that doesn't give enough information about the problem at hand. E-needs-mcve Call for participation: This issue has a repro, but needs a Minimal Complete and Verifiable Example 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

5 participants