-
Notifications
You must be signed in to change notification settings - Fork 13.9k
Description
I tried this code:
use socketioxide::{SocketIo, handler::ConnectHandler};
fn main() {}
async fn fails() {
let (_svc, io) = SocketIo::new_svc();
#[derive(Debug)]
struct MyError;
impl std::fmt::Display for MyError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "MyError")
}
}
let handler = || async move || Err(MyError);
io.ns(
"/",
{ async || {} }
.with(handler())
.with(handler())
.with(handler()),
);
}See the following repo for the minimal reproducible example:
https://github.com/Totodore/socketioxide-rust-compiler-1.90-error
I expected to see this happen: Finished dev profile [unoptimized + debuginfo] target(s) in 6.03s
Instead, compilation failed, see the backtrace section below for the complete error.
The compiler error only happens when switching to v1.90. It comes from this CI and I can also reproduce it on my PC:
https://github.com/Totodore/socketioxide/actions/runs/17881639090/job/50850045405#step:5:267
Meta
- The bug also happens in rust nightly 1.92
- Here is a minimal example, the issue seems to comes from the complex traits bounds used in socketioxide, therefore I did not succeed to replicate it without the socketioxide lib.
rustc --version --verbose:
rustc 1.90.0 (1159e78c4 2025-09-14)
binary: rustc
commit-hash: 1159e78c4747b02ef996e55082b704c09b970588
commit-date: 2025-09-14
host: x86_64-unknown-linux-gnu
release: 1.90.0
LLVM version: 20.1.8
Backtrace
error[E0391]: cycle detected when type-checking `fails`
--> src/main.rs:5:1
|
5 | async fn fails() {
| ^^^^^^^^^^^^^^^^
|
= note: ...which requires computing whether `<<socketioxide::handler::connect::ConnectHandler::with::{opaque#0} as socketioxide::handler::connect::ConnectHandler<socketioxide::adapter::LocalAdapter, (socketioxide::handler::connect::private::Async,)>>::with::{anon_assoc#0}<{async closure@src/main.rs:17:22: 17:35}, (socketioxide::handler::connect::private::Async,)> as socketioxide::handler::connect::ConnectHandler<socketioxide::adapter::LocalAdapter, (socketioxide::handler::connect::private::Async,)>>::with::{anon_assoc#0}<{async closure@src/main.rs:17:22: 17:35}, (socketioxide::handler::connect::private::Async,)>` is `Copy`...
= note: ...which requires evaluating trait selection obligation `<<socketioxide::handler::connect::ConnectHandler::with::{opaque#0} as socketioxide::handler::connect::ConnectHandler<socketioxide::adapter::LocalAdapter, (socketioxide::handler::connect::private::Async,)>>::with::{anon_assoc#0}<{async closure@src/main.rs:17:22: 17:35}, (socketioxide::handler::connect::private::Async,)> as socketioxide::handler::connect::ConnectHandler<socketioxide::adapter::LocalAdapter, (socketioxide::handler::connect::private::Async,)>>::with::{anon_assoc#0}<{async closure@src/main.rs:17:22: 17:35}, (socketioxide::handler::connect::private::Async,)>: core::marker::Copy`...
note: ...which requires looking up the hidden types stored across await points in a coroutine...
--> src/main.rs:17:36
|
17 | let handler = || async move || Err(MyError);
| ^^^^^^^^^^^^
note: ...which requires coroutine witness types for `fails::{closure#0}::{closure#0}::{closure#0}::{closure#0}`...
--> src/main.rs:17:36
|
17 | let handler = || async move || Err(MyError);
| ^^^^^^^^^^^^
note: ...which requires promoting constants in MIR for `fails::{closure#0}::{closure#0}::{closure#0}::{closure#0}`...
--> src/main.rs:17:36
|
17 | let handler = || async move || Err(MyError);
| ^^^^^^^^^^^^
note: ...which requires checking if `fails::{closure#0}::{closure#0}::{closure#0}::{closure#0}` contains FFI-unwind calls...
--> src/main.rs:17:36
|
17 | let handler = || async move || Err(MyError);
| ^^^^^^^^^^^^
note: ...which requires building MIR for `fails::{closure#0}::{closure#0}::{closure#0}::{closure#0}`...
--> src/main.rs:17:36
|
17 | let handler = || async move || Err(MyError);
| ^^^^^^^^^^^^
note: ...which requires match-checking `fails::{closure#0}::{closure#0}::{closure#0}::{closure#0}`...
--> src/main.rs:17:36
|
17 | let handler = || async move || Err(MyError);
| ^^^^^^^^^^^^
note: ...which requires type-checking `fails::{closure#0}::{closure#0}::{closure#0}::{closure#0}`...
--> src/main.rs:17:36
|
17 | let handler = || async move || Err(MyError);
| ^^^^^^^^^^^^
= note: ...which again requires type-checking `fails`, completing the cycle
note: cycle used when match-checking `fails`
--> src/main.rs:5:1
|
5 | async fn fails() {
| ^^^^^^^^^^^^^^^^
= note: see https://rustc-dev-guide.rust-lang.org/overview.html#queries and https://rustc-dev-guide.rust-lang.org/query.html for more information
For more information about this error, try `rustc --explain E0391`.
error: could not compile `socketioxide-compile-test-error` (bin "socketioxide-compile-test-error") due to 1 previous error
Side note
It seems that it is related to the Display bound of MyError. Also it is really weird because this bugs happens after three .with() chaining.
Bisection results
searched nightlies: from nightly-2025-08-01 to nightly-2025-09-20
regressed nightly: nightly-2025-08-03
searched commit range: 4b55fe1...a65b04d
regressed commit: 63f6845
bisected with cargo-bisect-rustc v0.6.10
Host triple: x86_64-unknown-linux-gnu
Reproduce with:
cargo bisect-rustc