-
Notifications
You must be signed in to change notification settings - Fork 13.8k
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