Skip to content
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

Tonic v0.13 fails to build what worked on v0.12 #1895

Open
bobrik opened this issue Aug 27, 2024 · 1 comment
Open

Tonic v0.13 fails to build what worked on v0.12 #1895

bobrik opened this issue Aug 27, 2024 · 1 comment

Comments

@bobrik
Copy link

bobrik commented Aug 27, 2024

I have the following function:

    async fn new_unix<P>(identity: String, path: P) -> Result<Self, Error>
    where
        P: AsRef<Path> + Send + Sync + Clone + 'static,
    {
        let connector = tower::service_fn(move |_| UnixStream::connect(path.clone()));

        let channel = Endpoint::try_from("http://[::]:0")
            .expect("empty addr should work")
            .connect_with_connector_lazy(connector);

        let inner = QsEdgeReadClient::new(channel).max_decoding_message_size(usize::MAX);

        Ok(Self { identity, inner })
    }

It works perfectly fine with these deps:

tonic = { version = "0.11" }
prost = { version = "0.12" }
prost-types = { version = "0.12" }

As soon as I bump them, errors happen:

error[E0277]: the trait bound `tokio::net::UnixStream: hyper::rt::io::Read` is not satisfied
   --> src/lib.rs:43:42
    |
43  |             .connect_with_connector_lazy(connector);
    |              --------------------------- ^^^^^^^^^ the trait `hyper::rt::io::Read` is not implemented for `tokio::net::UnixStream`
    |              |
    |              required by a bound introduced by this call
    |
    = help: the following other types implement trait `hyper::rt::io::Read`:
              &mut T
              Box<T>
              Pin<P>
              hyper::upgrade::Upgraded
              hyper_timeout::stream::TimeoutReader<R>
              hyper_timeout::stream::TimeoutStream<S>
              hyper_timeout::stream::TimeoutWriter<W>
              hyper_util::rt::tokio::TokioIo<T>
note: required by a bound in `Endpoint::connect_with_connector_lazy`
   --> /Users/ivan/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tonic-0.12.2/src/transport/channel/endpoint.rs:392:22
    |
389 |     pub fn connect_with_connector_lazy<C>(&self, connector: C) -> Channel
    |            --------------------------- required by a bound in this associated function
...
392 |         C::Response: rt::Read + rt::Write + Send + Unpin,
    |                      ^^^^^^^^ required by this bound in `Endpoint::connect_with_connector_lazy`

error[E0277]: the trait bound `tokio::net::UnixStream: hyper::rt::io::Write` is not satisfied
   --> src/lib.rs:43:42
    |
43  |             .connect_with_connector_lazy(connector);
    |              --------------------------- ^^^^^^^^^ the trait `hyper::rt::io::Write` is not implemented for `tokio::net::UnixStream`
    |              |
    |              required by a bound introduced by this call
    |
    = help: the following other types implement trait `hyper::rt::io::Write`:
              &mut T
              Box<T>
              Pin<P>
              hyper::upgrade::Upgraded
              hyper_timeout::stream::TimeoutReader<R>
              hyper_timeout::stream::TimeoutStream<S>
              hyper_timeout::stream::TimeoutWriter<W>
              hyper_util::rt::tokio::TokioIo<T>
note: required by a bound in `Endpoint::connect_with_connector_lazy`
   --> /Users/ivan/.cargo/registry/src/index.crates.io-6f17d22bba15001f/tonic-0.12.2/src/transport/channel/endpoint.rs:392:33
    |
389 |     pub fn connect_with_connector_lazy<C>(&self, connector: C) -> Channel
    |            --------------------------- required by a bound in this associated function
...
392 |         C::Response: rt::Read + rt::Write + Send + Unpin,
    |                                 ^^^^^^^^^ required by this bound in `Endpoint::connect_with_connector_lazy`

I tried my best to figure out a way to make this work based on the uds example:

        let channel = Endpoint::try_from("http://[::]:0")
            .expect("empty addr should work")
            .connect_with_connector(tower::service_fn(|_: Uri| async {
                Ok::<_, std::io::Error>(TokioIo::new(UnixStream::connect(path).await?))
            }))
            .await?;
error[E0525]: expected a closure that implements the `FnMut` trait, but this closure only implements `FnOnce`
  --> src/lib.rs:48:55
   |
48 |             .connect_with_connector(tower::service_fn(|_: Uri| async {
   |              ----------------------                   ^^^^^^^^ this closure implements `FnOnce`, not `FnMut`
   |              |
   |              the requirement to implement `FnMut` derives from here
49 |                 Ok::<_, std::io::Error>(TokioIo::new(UnixStream::connect(path).await?))
   |                                                                          ---- closure is `FnOnce` because it moves the variable `path` out of its environment
   |
   = note: required for `ServiceFn<{closure@src/lib.rs:48:55: 48:63}>` to implement `Service<Uri>`
        let channel = Endpoint::try_from("http://[::]:0")
            .expect("empty addr should work")
            .connect_with_connector(tower::service_fn(move |_: Uri| async {
                Ok::<_, std::io::Error>(TokioIo::new(UnixStream::connect(path.clone()).await?))
            }))
            .await?;
error: lifetime may not live long enough
  --> src/lib.rs:48:69
   |
48 |               .connect_with_connector(tower::service_fn(move |_: Uri| async {
   |  _______________________________________________________-------------_^
   | |                                                       |           |
   | |                                                       |           return type of closure `{async block@src/lib.rs:48:69: 50:14}` contains a lifetime `'2`
   | |                                                       lifetime `'1` represents this closure's body
49 | |                 Ok::<_, std::io::Error>(TokioIo::new(UnixStream::connect(path.clone()).await?))
50 | |             }))
   | |_____________^ returning this value requires that `'1` must outlive `'2`
   |
   = note: closure implements `Fn`, so references to captured variables can't escape the closure

The uds example in the repo conveniently avoids this problem by having path declared in the closure itself. It would be nice to have a more realistic example that takes the path from the outside.

@bobrik
Copy link
Author

bobrik commented Sep 22, 2024

I think I figured out the lifetime issue: path needs to be cloned outside of async {}.

        let connector = tower::service_fn(move |_| {
            let path = path.clone();
            async { Ok::<_, std::io::Error>(TokioIo::new(UnixStream::connect(path).await?)) }
        });

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant