From 281f7539a03d5910b38fb3a5461f0460d5e3710b Mon Sep 17 00:00:00 2001 From: Roman Volosatovs Date: Tue, 30 Jun 2026 16:51:34 +0200 Subject: [PATCH] refactor: remove internal Captures workaround trait `wrpc-transport` carried a `#[doc(hidden)] Captures<'a>` marker trait as a workaround for rust-lang/rust#63033, used to force returned `impl Future`s to capture an otherwise-unused lifetime. Precise capturing (`use<..>`, stable since Rust 1.82) makes it unnecessary. Drop the trait and its uses: - transport: remove the trait/impl and the `serve` test bounds (edition 2024 captures the elided lifetime by default). - rust bindgen: replace `+ Captures<'a>` on generated `serve`/`serve_interface` futures with `+ use<'a, T, H>`, turning the argument-position `impl Handler` into a named generic so it can be named in the `use<..>` bound; the nested deferred futures now use `+ use<'a, C__>`. Generated bindings continue to compile under both edition 2021 and 2024. Assisted-by: claude:claude-opus-4-8 --- crates/transport/src/lib.rs | 9 --------- crates/transport/src/serve.rs | 6 ++---- crates/wit-bindgen-rust/src/interface.rs | 12 +++++------- crates/wit-bindgen-rust/src/lib.rs | 6 +++--- 4 files changed, 10 insertions(+), 23 deletions(-) diff --git a/crates/transport/src/lib.rs b/crates/transport/src/lib.rs index e22be10a6..f6cab2ea0 100644 --- a/crates/transport/src/lib.rs +++ b/crates/transport/src/lib.rs @@ -40,15 +40,6 @@ use bytes::BytesMut; use tokio::io::{AsyncRead, ReadBuf}; use tracing::trace; -/// Internal workaround trait -/// -/// This is an internal trait used as a workaround for -/// https://github.com/rust-lang/rust/issues/63033 -#[doc(hidden)] -pub trait Captures<'a> {} - -impl Captures<'_> for T {} - /// Buffered incoming stream used for decoding values /// /// This wraps the multiplexed framed [`frame::Incoming`] stream with a read buffer used to diff --git a/crates/transport/src/serve.rs b/crates/transport/src/serve.rs index ef27e7e6a..3e03cbbb0 100644 --- a/crates/transport/src/serve.rs +++ b/crates/transport/src/serve.rs @@ -156,8 +156,6 @@ mod tests { use bytes::Bytes; use futures::{StreamExt as _, TryStreamExt as _, stream}; - use crate::Captures; - use super::*; async fn call_serve(s: &T) -> anyhow::Result> { @@ -198,7 +196,7 @@ mod tests { s: &T, ) -> impl Future< Output = anyhow::Result> + 'static>>>, - > + Captures<'_> { + > { let fut = s.serve( "foo", "bar", @@ -215,7 +213,7 @@ mod tests { s: &T, ) -> impl Future< Output = anyhow::Result> + 'static>>>, - > + crate::Captures<'_> { + > { let fut = s.serve_values::<(Bytes,), (Bytes,)>( "foo", "bar", diff --git a/crates/wit-bindgen-rust/src/interface.rs b/crates/wit-bindgen-rust/src/interface.rs index afa24a521..d7cf30961 100644 --- a/crates/wit-bindgen-rust/src/interface.rs +++ b/crates/wit-bindgen-rust/src/interface.rs @@ -143,9 +143,9 @@ impl InterfaceGenerator<'_> { self.src, r#" #[allow(clippy::manual_async_fn)] -pub fn serve_interface<'a, T: {wrpc_transport}::Serve>( +pub fn serve_interface<'a, T: {wrpc_transport}::Serve, H: Handler + {resource_traits} ::core::marker::Send + ::core::marker::Sync + ::core::clone::Clone + 'static>( wrpc: &'a T, - handler: impl Handler + {resource_traits} ::core::marker::Send + ::core::marker::Sync + ::core::clone::Clone + 'static, + handler: H, ) -> impl ::core::future::Future< Output = {anyhow}::Result< [ @@ -171,7 +171,7 @@ pub fn serve_interface<'a, T: {wrpc_transport}::Serve>( {n} ] > - > + ::core::marker::Send + {wrpc_transport}::Captures<'a> {{ + > + ::core::marker::Send + use<'a, T, H> {{ async move {{ let ("#, resource_traits = trait_names.join(""), @@ -602,8 +602,7 @@ pub fn serve_interface<'a, T: {wrpc_transport}::Serve>( if async_params || !paths.is_empty() { uwrite!( self.src, - ", ::core::option::Option> + ::core::marker::Send + 'static + {wrpc_transport}::Captures<'a>>)", - wrpc_transport = self.r#gen.wrpc_transport_path(), + ", ::core::option::Option> + ::core::marker::Send + 'static + use<'a, C__>>)", ); } uwrite!(self.src, ">> + Send + 'a"); @@ -620,8 +619,7 @@ pub fn serve_interface<'a, T: {wrpc_transport}::Serve>( if async_params || !paths.is_empty() { uwrite!( self.src, - "::core::option::Option> + ::core::marker::Send + 'static + {wrpc_transport}::Captures<'a>>", - wrpc_transport = self.r#gen.wrpc_transport_path(), + "::core::option::Option> + ::core::marker::Send + 'static + use<'a, C__>>", ); } uwrite!(self.src, ")>> + Send + 'a"); diff --git a/crates/wit-bindgen-rust/src/lib.rs b/crates/wit-bindgen-rust/src/lib.rs index 341fab7ae..b5b833d0c 100644 --- a/crates/wit-bindgen-rust/src/lib.rs +++ b/crates/wit-bindgen-rust/src/lib.rs @@ -428,9 +428,9 @@ with: {{\n\t{with_name:?}: generate\n}} self.src, r#" #[allow(clippy::manual_async_fn)] -pub fn serve<'a, T: {wrpc_transport}::Serve>( +pub fn serve<'a, T: {wrpc_transport}::Serve, H: {bound} + ::core::marker::Send + ::core::marker::Sync + ::core::clone::Clone + 'static>( wrpc: &'a T, - handler: impl {bound} + ::core::marker::Send + ::core::marker::Sync + ::core::clone::Clone + 'static, + handler: H, ) -> impl ::core::future::Future< Output = {anyhow}::Result< ::std::vec::Vec< @@ -455,7 +455,7 @@ pub fn serve<'a, T: {wrpc_transport}::Serve>( ) > > - > + ::core::marker::Send + {wrpc_transport}::Captures<'a> {{ + > + ::core::marker::Send + use<'a, T, H> {{ async move {{ let interfaces = {tokio}::try_join!("# );