Error: one type is more general than the other

Getting error one type is more general than the other
No idea why & how to reproduce.

Can you please reply all the possible scenario for this error.
Thank you

1 Like

I believe this generally occurs when using types involving lifetimes, and two types look very similar, but one has a specific lifetime, and the other references any lifetime.

All the examples I can find are when using function types, either dyn Fn() or fn().

For instance, see this test case in rust-lang:


// In this fn, the type `F` is a function that takes a reference to a
// struct and returns another reference with the same lifetime.
//
// Meanwhile, the bare fn `foo` takes a reference to a struct with
// *ANY* lifetime and returns a reference with the 'static lifetime.
// This can safely be considered to be an instance of `F` because all
// lifetimes are sublifetimes of 'static.

#![allow(dead_code)]
#![allow(unused_variables)]

struct S;

// Given 'cx, return 'cx
type F = for<'cx> fn(&'cx S) -> &'cx S;
fn want_F(f: F) {}

// Given anything, return 'static
type G = for<'cx> fn(&'cx S) -> &'static S;
fn want_G(f: G) {}

// Should meet both.
fn foo(x: &S) -> &'static S {
    panic!()
}

// Should meet both.
fn bar<'a, 'b>(x: &'a S) -> &'b S {
    panic!()
}

// Meets F, but not G.
fn baz(x: &S) -> &S {
    panic!()
}

fn supply_F() {
    want_F(foo);

    want_F(bar);

    want_F(baz);
}

fn supply_G() {
    want_G(foo);
    want_G(bar);
    want_G(baz); //~ ERROR mismatched types
}

pub fn main() {}

It's... not super clear, as it's written as a test case, but it's an easily accessible reference I found.

As for all places this error occurs, I'm not sure. This is definitely one case, though, at least.

1 Like

To illustrate with something hopefully more concrete, consider the following snippet:

use ::parking_lot::{const_mutex, Mutex};

static GLOBAL: Mutex<&'static str> = const_mutex("");

fn takes_static_str (s: &'static str)
{
    *GLOBAL.lock() = s;
}

fn feed_local_str (
    cb: for<'any> fn(&'any str),
)
{
    let local_string = String::from("local");
    cb(&local_string);
 // drop(local_string);
}

fn main ()
{
    feed_local_str(takes_static_str);
    println!("{}", &*GLOBAL.lock());
}

Now, imagine if this code compiled: we'd be reading a dangling reference from the GLOBAL, since it would have been feed a reference to the local_string which is dropped before feed_local_str returns.

Luckily, the compiler, as always, isn't fooled, and sees that there is an issue. The way it sees so is that:

  1. feed_local_str is a function that excepts a callback (here, a function pointer), that must be capable of handling a reference to a str that lasts for 'any duration, even an arbitrarily small one

    • (this is indeed necessary for the body of feed_local_str to work: the duration of the borrow is determined by the body of the function, not by the callsite, which means the lifetime parameter there, 'any, needs to be a higher-order / universal one rather than a generic parameter: with a signature such as fn feed_local_str<'r> (cb: fn(&'r str)), Rust would complain that local_string does not live long enough).
  2. And yet takes_static_str is a function that is only capable of taking, as its name indicates, &'static strs, since indeed it takes advantage of that restriction to let those references "escape" into a global.

So, we end up with the feed_local_str(takes_static_str) call opposing a:

  • for<'any> fn(&'any str) signature requirement,

  • to a fn(&'static str) actual parameter.

The latter is thus too restrictive / does not meet the more general higher-order signature requirement. Hence the error: a legitimate one.

error[E0308]: mismatched types
  --> src/main.rs:21:20
   |
21 |     feed_local_str(takes_static_str);
   |                    ^^^^^^^^^^^^^^^^ one type is more general than the other
   |
   = note: expected fn pointer `for<'any> fn(&'any str)`
              found fn pointer `fn(&'static str)`
1 Like

No, not really. A given error message can be achieved in many different ways. You'll have to show us the code that produces the error message.

Hi @H2CO3 @Yandros @daboross

This is the open source project repo. If you wants to contribute, you can. Very small LOC so would not take more than 5 minutes to understand.

If you clone & run the cargo run. you will get the error.

For context, here's the full error:

error[E0308]: mismatched types
  --> src/main.rs:21:9
   |
21 |     app.get("/", index);
   |         ^^^ lifetime mismatch
   |
   = note: expected associated type `<for<'_> fn(&mut structs::Context) -> impl futures::Future<Output = ()> {index} as FnOnce<(&mut structs::Context,)>>::Output`
              found associated type `<for<'_> fn(&mut structs::Context) -> impl futures::Future<Output = ()> {index} as FnOnce<(&mut structs::Context,)>>::Output`
   = note: the required lifetime does not necessarily outlive the static lifetime
note: the lifetime requirement is introduced here
  --> src/server.rs:70:32
   |
70 |         F: Fn(&mut Context) -> Fcb + 'static + Send,
   |                                ^^^

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

Here's the code erroring:

async fn index(ctx: &mut Context) -> () {
    ctx.response.body = "Hello".to_string();
    p_hello().await;
}

fn main() {
    let mut app = Server::new();
    app.get("/", index);
    app.listen("0.0.0.0:3000");
}

And here's the library code being called:

    pub fn get<F, Fcb>(&mut self, path: &str, callback: F) -> ()
    where
        F: Fn(&mut Context) -> Fcb + 'static + Send,
        Fcb: Future<Output = ()> + 'static + Send,
    {
        self.gets.push((
            path.to_string(),
            Arc::new(Mutex::new(Box::new(move |_| {
                Box::pin(callback(&mut default_context()))
            }))),
        ));
    }

Long story short, the future created by the index function does not live for 'static.

Long story long, the problem is with how long Context lives. Specifically, in get, you are creating a new Context, giving a borrow of that context to your calback, and then expecting that callback to outlive your current function with a 'static bound.

However, index, being an async function, creates a future containing all of its contents. The problem is that the borrow of Context is part of those contents! Index is equivalent to something like this:

fn index<'a>(ctx: &'a mut Context) -> MyFuture<'a> {
    struct MyFuture<'x> {
        ctx: &'x mut Context,
        // other details to handle p_hello()
    }
    impl<'x> Future for MyFuture<'x> {
        fn poll(self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
             // skimming the details here
            ctx.response.body = "Hello".to_string();
            // other details to handle p_hello().await;
        }
    }

    MyFuture { ctx: ctx }
}

What I'm saying with this code snippet is that the future created by executing index continues to hold that &mut Context reference, as it was an argument to the async fn.

So, when you then expect index to outlive the Context you created by requiring it to satisfy the 'static bound, it doesn't work.

To fix this you need to either eliminate the borrowed Context (e.i. give it an owned vers What I'm saying is that the future created by executing index continues to hold that &mut Context reference, as it was an argument to the async fn . What I'm saying is that the future created by executing index continues to hold that &mut Context reference, as it was an argument to the async fn .ion of Context or handle context some other way), or stop requiring it to outlive the Context you create (remove the 'static bounds, then find some way to use it that doesn't involve storing it somewhere where it lives longer than where-ever its Context is stored).

Hope that helps?

2 Likes

Thank you
Yes it does make sense

1 Like

Glad to help!

If you'd be interested in making a bug report, I think this could definitely be a candidate for a "bad error message" bug: https://github.com/rust-lang/rust/issues/new?assignees=&labels=A-diagnostics%2C+T-compiler&template=diagnostics.md

I'm not exactly sure what a better error message would be, but if we could find one I think the compiler team would be happy to have another case to give better diagnostics for.

1 Like

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.