-
Notifications
You must be signed in to change notification settings - Fork 13.3k
Unhelpful error "one type is more general than the other" in async code #64650
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
Comments
Here's an updated reproduction that doesn't use the // Using futures-preview v0.3.0-alpha.19
use futures::StreamExt;
fn main() {
enter(run())
}
async fn run() {
let entries: Vec<_> = vec![()]
.into_iter()
.map(|x| async { vec![()].into_iter().map(|_| x) })
.collect::<futures::stream::FuturesUnordered<_>>()
.map(futures::stream::iter)
.flatten()
.collect()
.await;
}
pub fn enter(x: impl Send) {} Error:
ructc version: |
Someone was just asking about this in hyper's gitter channel. The diagnostics here are surprisingly bad. Ping? |
The output is better on nightly:
but still contains the spurious "mismatched types" error. |
I think the 'one type is more general than the other' error is another instance of #64552 |
This doesn't include async, but the conditions are similar enough, and I found it equally confusing (especially once I realized what the fix was): https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=f2cf25b58f393f4a4377b0086e0032a2 inline: #[derive(Clone)]
struct A;
trait CloneableFn: Fn(&A) + CloneBox {}
trait CloneBox {
fn clone_box(&self) -> Box<dyn CloneableFn>;
}
impl<T> CloneableFn for T where T: Fn(&A) + CloneBox {}
impl<T> CloneBox for T where T: Fn(&A) + Clone + 'static {
fn clone_box(&self) -> Box<dyn CloneableFn> {
Box::new(self.clone()) as Box<dyn CloneableFn>
}
}
fn take_cfn(cfn: Box<dyn CloneableFn>, a: A) {
cfn(&a);
}
fn main() {
let a = A;
let closure = move |_| { // solve with `move |_: &A|`
let _b = a.clone();
};
take_cfn(Box::new(closure) as Box<dyn CloneableFn>, A);
} Error:
|
I ran into this same issue this week using tokio. Here's my conclusion, in case it's helpful for narrowing in on the bug. Reduced down to as few lines as possible it looks like this: runtime.spawn(async {
warp::serve(
routes
.or(other_routes)
.or(more_routes)
.map(|reply| {
warp::reply::with_header(reply, "Sec-WebSocket-Protocol", "graphql-ws")
})
)
.run(([0, 0, 0, 0], 3001))
.await;
}); and fails with: --> src/lib.rs:185:14
|
185 | runtime.spawn(async {
| ^^^^^ one type is more general than the other
|
= note: expected type `std::ops::FnOnce<(warp::generic::Either<(warp::generic::Either<(warp::http::Response<std::vec::Vec<u8>>,), (warp::http::Response<std::vec::Vec<u8>>,)>,), (impl warp::Reply,)>,)>`
found type `std::ops::FnOnce<(warp::generic::Either<(warp::generic::Either<(warp::http::Response<std::vec::Vec<u8>>,), (warp::http::Response<std::vec::Vec<u8>>,)>,), (impl warp::Reply,)>,)>` Those types are identical aren't they? I managed to work around it like this: + .with(warp::reply::with::header("Sec-WebSocket-Protocol", "graphql-ws"))
- .map(|reply| {
- warp::reply::with_header(reply, "Sec-WebSocket-Protocol", "graphql-ws")
- }) The first version has been working fine in another part of the codebase, something about how it's called here triggers it maybe? The other way I'm using it is in a |
I have found a fairly minimal way to reproduce without external dependencies, and several useful observations. It's not using async, so maybe this is more fitted for #64552? Tested with This is the reproduction code: pub struct BoxHolder<BoxedFn> {
data: BoxedFn,
}
impl<Ret> BoxHolder<Box<fn(&str) -> Ret>> {
pub fn box_and_set_data(&mut self, ptr: fn(&str) -> Ret) {
let pre_boxed: Box<fn(&str) -> Ret> = Box::new(ptr); // box contents have concrete size, type annotations are for clarity
self.data = pre_boxed; // not the case here
}
}
fn a(a: &str) -> &str {
""
}
fn main() {
let mut foo: BoxHolder<Box<fn(&str) -> &str>> = BoxHolder { data: Box::new(a) };
foo.box_and_set_data(a);
} The above yields the following error:
Replacing the generic From the error it looks like a lifetime is either forgotten or elided. Removing the type annotations on fn main() {
// let mut foo: BoxHolder<Box<fn(&str) -> &str>> = BoxHolder { data: Box::new(a) };
let mut foo = BoxHolder { data: Box::new(a) };
foo.box_and_set_data(a);
} And the new error:
Well that's not right. What explicitly declaring the lifetime of // fn a(a: &str) -> &str {
fn a(a: &str) -> &'static str {
""
}
fn main() {
let mut foo = BoxHolder { data: Box::new(a) };
foo.box_and_set_data(a);
} New error:
Now the lifetimes are correct but it's not picking up the impl BoxHolder<Box<for<'r> fn(&'r str) -> &'static str>> {
pub fn box_and_set_data(&mut self, ptr: for<'r> fn(&'r str) -> &'static str) {
self.data = Box::new(ptr);
}
}
fn a(a: &str) -> &'static str {
""
}
fn main() {
let mut foo = BoxHolder { data: Box::new(a) };
foo.box_and_set_data(a);
} New error:
Even though the pub struct BoxHolder<BoxedFn> {
data: BoxedFn,
}
impl BoxHolder<Box<fn(&str) -> &str>> {
pub fn box_and_set_data(&mut self, ptr: fn(&str) -> &str) {
self.data = Box::new(ptr);
}
}
fn a(a: &str) -> &str {
""
}
fn main() {
let mut foo: BoxHolder<Box<fn(&str) -> &str>> = BoxHolder { data: Box::new(a) };
foo.box_and_set_data(a);
} The above code compiles correctly. It seems that FWIW, doing fn a(a: &str) -> &str {
""
}
fn main() {
let a1 = a;
let a2: fn(&str) -> &str = a;
} This is all the useful information I'm able to gleam from this. I'm unsure if the later results are directly related to the initial |
The following code does not compile:
It produces this error message:
In this case, the code can be made to compile by adding
move
in two places on this line:Some possibly related issues: #57362, #60658
rustc version:
1.39.0-nightly (97e58c0d3 2019-09-20)
The text was updated successfully, but these errors were encountered: