I’m trying to implement a trait WebsocketIO
for T
but it always says the trait bounds were not satisfied when I’m trying to use it on my AsyncWebsocketClient
. AsyncWebsocketClient
is supposed to work with tokio-tungstenite.
Here is the libraries Cargo.toml
[package]
name = "websocket"
version = "0.1.0"
edition = "2021"
[dependencies]
embassy-futures = "0.1.1"
embassy-sync = "0.6.0"
futures = "0.3.30"
tokio-tungstenite = { version = "0.23.1", features = ["native-tls"] }
Here is the AsyncWebsocketClient
:
pub struct AsyncWebsocketClient<'a, T, Status = WebsocketClosed> {
inner: Arc<Mutex<&'a mut T>>,
status: PhantomData<Status>,
}
It implements futures::Stream
and futures::Sink
:
impl<T> Stream for AsyncWebsocketClient<'_, T, WebsocketOpen>
where
T: Stream<Item = Message> + Unpin
{
type Item = Message;
fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
let mut stream = block_on(self.inner.lock());
Pin::new(&mut *stream).poll_next(cx)
}
}
impl<T> Sink<Message> for AsyncWebsocketClient<'_, T, WebsocketOpen>
where
T: Sink<Message, Error = tokio_tungstenite::tungstenite::Error> + Unpin
{
type Error = tokio_tungstenite::tungstenite::Error;
fn poll_ready(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let mut stream = block_on(self.inner.lock());
Pin::new(&mut *stream).poll_ready(cx)
}
fn start_send(self: Pin<&mut Self>, item: Message) -> Result<(), Self::Error> {
let mut stream = block_on(self.inner.lock());
Pin::new(&mut *stream).start_send(item)
}
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let mut stream = block_on(self.inner.lock());
Pin::new(&mut *stream).poll_flush(cx)
}
fn poll_close(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
let mut stream = block_on(self.inner.lock());
Pin::new(&mut *stream).poll_close(cx)
}
}
Here is the trait WebsocketIO
which I want to implement for T
. Similar to futures::SinkExt
and futures::StreamExt
.
pub trait WebsocketIO {
async fn t_send(&mut self, message: Message) -> Result<(), tokio_tungstenite::tungstenite::Error>;
async fn t_receive(&mut self) -> Option<Message>;
}
The implementation for T
:
impl<T: ?Sized> WebsocketIO for T
where
T: Stream<Item = Message> + Sink<Message, Error = tokio_tungstenite::tungstenite::Error> + Unpin
{
async fn t_send(&mut self, message: Message) -> Result<(), tokio_tungstenite::tungstenite::Error> {
use futures::SinkExt;
self.send(message).await
}
async fn t_receive(&mut self) -> Option<Message> {
use futures::StreamExt;
self.next().await
}
}
This is a simplified version of the actual AsyncWebsocketClient
but is sufficient to reproduce the error. While the implementation works in a #[tokio::test]
scope inside my library, the compiler throws an error as soon as I try to use my library in a project as a dependency. This is my example Cargo.toml:
[package]
name = "_std"
version = "0.1.0"
edition = "2021"
[dependencies]
websocket = { path = "../.."} # my library
tokio = { version = "1.38.0", features = ["full"] }
tokio-tungstenite = { version = "0.23.1", features = ["native-tls"] }
[[bin]]
name = "client"
Here is the example code I’m trying to test my AsyncWebsocketClient
with:
use tokio_tungstenite::{connect_async, tungstenite::Message};
use websocket::{AsyncWebsocketClient, WebsocketIO};
#[tokio::main]
async fn main() {
// connect to echo websocket server
let mut stream = connect_async("wss://ws.postman-echo.com/raw")
.await
.expect("Failed to connect")
.0;
let mut client = AsyncWebsocketClient::open(&mut stream);
client.t_send(Message::Text("Hello, world!".to_string())).await.unwrap();
}
Here is the error I’m getting due to unsatisfied trait bounds:
vscode ➜ /workspaces/rust/examples/std (master) $ cargo run --bin client
warning: unused import: `StreamExt`
--> /workspaces/rust/src/lib.rs:4:62
|
4 | use futures::{executor::block_on, lock::Mutex, Sink, Stream, StreamExt};
| ^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
warning: use of `async fn` in public traits is discouraged as auto trait bounds cannot be specified
--> /workspaces/rust/src/lib.rs:66:5
|
66 | async fn t_send(&mut self, message: Message) -> Result<(), tokio_tungstenite::tungstenite::Error>;
| ^^^^^
|
= note: you can suppress this lint if you plan to use the trait only in your own code, or do not care about auto traits like `Send` on the `Future`
= note: `#[warn(async_fn_in_trait)]` on by default
help: you can alternatively desugar to a normal `fn` that returns `impl Future` and add any desired bounds such as `Send`, but these cannot be relaxed without a breaking API change
|
66 - async fn t_send(&mut self, message: Message) -> Result<(), tokio_tungstenite::tungstenite::Error>;
66 + fn t_send(&mut self, message: Message) -> impl std::future::Future<Output = Result<(), tokio_tungstenite::tungstenite::Error>> + Send;
|
warning: use of `async fn` in public traits is discouraged as auto trait bounds cannot be specified
--> /workspaces/rust/src/lib.rs:67:5
|
67 | async fn t_receive(&mut self) -> Option<Message>;
| ^^^^^
|
= note: you can suppress this lint if you plan to use the trait only in your own code, or do not care about auto traits like `Send` on the `Future`
help: you can alternatively desugar to a normal `fn` that returns `impl Future` and add any desired bounds such as `Send`, but these cannot be relaxed without a breaking API change
|
67 - async fn t_receive(&mut self) -> Option<Message>;
67 + fn t_receive(&mut self) -> impl std::future::Future<Output = Option<Message>> + Send;
|
warning: `websocket` (lib) generated 3 warnings
Compiling _std v0.1.0 (/workspaces/rust/examples/std)
error[E0599]: the method `t_send` exists for struct `AsyncWebsocketClient<'_, WebSocketStream<MaybeTlsStream<TcpStream>>, WebsocketOpen>`, but its trait bounds were not satisfied
--> src/bin/client.rs:14:12
|
14 | client.t_send(Message::Text("Hello, world!".to_string())).await.unwrap();
| ^^^^^^
|
::: /workspaces/rust/src/lib.rs:12:1
|
12 | pub struct AsyncWebsocketClient<'a, T, Status = WebsocketClosed> {
| ---------------------------------------------------------------- doesn't satisfy `<_ as Stream>::Item = Message`, `_: Stream` or `_: WebsocketIO`
|
= note: the following trait bounds were not satisfied:
`<AsyncWebsocketClient<'_, WebSocketStream<tokio_tungstenite::MaybeTlsStream<tokio::net::TcpStream>>, WebsocketOpen> as futures_core::stream::Stream>::Item = Message`
which is required by `AsyncWebsocketClient<'_, WebSocketStream<tokio_tungstenite::MaybeTlsStream<tokio::net::TcpStream>>, WebsocketOpen>: WebsocketIO`
`AsyncWebsocketClient<'_, WebSocketStream<tokio_tungstenite::MaybeTlsStream<tokio::net::TcpStream>>, WebsocketOpen>: futures_core::stream::Stream`
which is required by `AsyncWebsocketClient<'_, WebSocketStream<tokio_tungstenite::MaybeTlsStream<tokio::net::TcpStream>>, WebsocketOpen>: WebsocketIO`
warning: unused import: `WebsocketIO`
--> src/bin/client.rs:4:39
|
4 | use websocket::{AsyncWebsocketClient, WebsocketIO};
| ^^^^^^^^^^^
|
= note: `#[warn(unused_imports)]` on by default
For more information about this error, try `rustc --explain E0599`.
warning: `_std` (bin "client") generated 1 warning
error: could not compile `_std` (bin "client") due to 1 previous error; 1 warning emitted
I can’t explain to myself why the compiler tells me that Stream
and Sink
are not implemented for AsyncWebsocketClient<‘_, WebSocketStream<tokio_tungstenite::MaybeTlsStream<tokio::net::TcpStream>>, WebsocketOpen>.
I also checked if it was a version mismatch but I couldn’t find any. Here is my cargo-tree output:
vscode ➜ /workspaces/rust/examples/std (master) $ cargo tree
_std v0.1.0 (/workspaces/rust/examples/std)
├── tokio v1.38.0
│ ├── bytes v1.6.0
│ ├── libc v0.2.155
│ ├── mio v0.8.11
│ │ └── libc v0.2.155
│ ├── num_cpus v1.16.0
│ │ └── libc v0.2.155
│ ├── parking_lot v0.12.3
│ │ ├── lock_api v0.4.12
│ │ │ └── scopeguard v1.2.0
│ │ │ [build-dependencies]
│ │ │ └── autocfg v1.3.0
│ │ └── parking_lot_core v0.9.10
│ │ ├── cfg-if v1.0.0
│ │ ├── libc v0.2.155
│ │ └── smallvec v1.13.2
│ ├── pin-project-lite v0.2.14
│ ├── signal-hook-registry v1.4.2
│ │ └── libc v0.2.155
│ ├── socket2 v0.5.7
│ │ └── libc v0.2.155
│ └── tokio-macros v2.3.0 (proc-macro)
│ ├── proc-macro2 v1.0.86
│ │ └── unicode-ident v1.0.12
│ ├── quote v1.0.36
│ │ └── proc-macro2 v1.0.86 (*)
│ └── syn v2.0.68
│ ├── proc-macro2 v1.0.86 (*)
│ ├── quote v1.0.36 (*)
│ └── unicode-ident v1.0.12
├── tokio-tungstenite v0.23.1
│ ├── futures-util v0.3.30
│ │ ├── futures-channel v0.3.30
│ │ │ ├── futures-core v0.3.30
│ │ │ └── futures-sink v0.3.30
│ │ ├── futures-core v0.3.30
│ │ ├── futures-io v0.3.30
│ │ ├── futures-macro v0.3.30 (proc-macro)
│ │ │ ├── proc-macro2 v1.0.86 (*)
│ │ │ ├── quote v1.0.36 (*)
│ │ │ └── syn v2.0.68 (*)
│ │ ├── futures-sink v0.3.30
│ │ ├── futures-task v0.3.30
│ │ ├── memchr v2.7.4
│ │ ├── pin-project-lite v0.2.14
│ │ ├── pin-utils v0.1.0
│ │ └── slab v0.4.9
│ │ [build-dependencies]
│ │ └── autocfg v1.3.0
│ ├── log v0.4.21
│ ├── native-tls v0.2.12
│ │ ├── log v0.4.21
│ │ ├── openssl v0.10.64
│ │ │ ├── bitflags v2.6.0
│ │ │ ├── cfg-if v1.0.0
│ │ │ ├── foreign-types v0.3.2
│ │ │ │ └── foreign-types-shared v0.1.1
│ │ │ ├── libc v0.2.155
│ │ │ ├── once_cell v1.19.0
│ │ │ ├── openssl-macros v0.1.1 (proc-macro)
│ │ │ │ ├── proc-macro2 v1.0.86 (*)
│ │ │ │ ├── quote v1.0.36 (*)
│ │ │ │ └── syn v2.0.68 (*)
│ │ │ └── openssl-sys v0.9.102
│ │ │ └── libc v0.2.155
│ │ │ [build-dependencies]
│ │ │ ├── cc v1.0.101
│ │ │ ├── pkg-config v0.3.30
│ │ │ └── vcpkg v0.2.15
│ │ ├── openssl-probe v0.1.5
│ │ └── openssl-sys v0.9.102 (*)
│ ├── tokio v1.38.0 (*)
│ ├── tokio-native-tls v0.3.1
│ │ ├── native-tls v0.2.12 (*)
│ │ └── tokio v1.38.0 (*)
│ └── tungstenite v0.23.0
│ ├── byteorder v1.5.0
│ ├── bytes v1.6.0
│ ├── data-encoding v2.6.0
│ ├── http v1.1.0
│ │ ├── bytes v1.6.0
│ │ ├── fnv v1.0.7
│ │ └── itoa v1.0.11
│ ├── httparse v1.9.4
│ ├── log v0.4.21
│ ├── native-tls v0.2.12 (*)
│ ├── rand v0.8.5
│ │ ├── libc v0.2.155
│ │ ├── rand_chacha v0.3.1
│ │ │ ├── ppv-lite86 v0.2.17
│ │ │ └── rand_core v0.6.4
│ │ │ └── getrandom v0.2.15
│ │ │ ├── cfg-if v1.0.0
│ │ │ └── libc v0.2.155
│ │ └── rand_core v0.6.4 (*)
│ ├── sha1 v0.10.6
│ │ ├── cfg-if v1.0.0
│ │ ├── cpufeatures v0.2.12
│ │ └── digest v0.10.7
│ │ ├── block-buffer v0.10.4
│ │ │ └── generic-array v0.14.7
│ │ │ └── typenum v1.17.0
│ │ │ [build-dependencies]
│ │ │ └── version_check v0.9.4
│ │ └── crypto-common v0.1.6
│ │ ├── generic-array v0.14.7 (*)
│ │ └── typenum v1.17.0
│ ├── thiserror v1.0.61
│ │ └── thiserror-impl v1.0.61 (proc-macro)
│ │ ├── proc-macro2 v1.0.86 (*)
│ │ ├── quote v1.0.36 (*)
│ │ └── syn v2.0.68 (*)
│ └── utf-8 v0.7.6
└── websocket v0.1.0 (/workspaces/rust)
├── embassy-futures v0.1.1
├── embassy-sync v0.6.0
│ ├── cfg-if v1.0.0
│ ├── critical-section v1.1.2
│ ├── embedded-io-async v0.6.1
│ │ └── embedded-io v0.6.1
│ ├── futures-util v0.3.30 (*)
│ └── heapless v0.8.0
│ ├── hash32 v0.3.1
│ │ └── byteorder v1.5.0
│ └── stable_deref_trait v1.2.0
├── futures v0.3.30
│ ├── futures-channel v0.3.30 (*)
│ ├── futures-core v0.3.30
│ ├── futures-executor v0.3.30
│ │ ├── futures-core v0.3.30
│ │ ├── futures-task v0.3.30
│ │ └── futures-util v0.3.30 (*)
│ ├── futures-io v0.3.30
│ ├── futures-sink v0.3.30
│ ├── futures-task v0.3.30
│ └── futures-util v0.3.30 (*)
└── tokio-tungstenite v0.23.1 (*)
For a better understanding here is also my project tree:
rust
├─ .devcontainer
│ └─ devcontainer.json
├─ .git
│ ├─ FETCH_HEAD
│ ├─ HEAD
│ ├─ config
│ ├─ description
│ ├─ hooks
│ │ ├─ applypatch-msg.sample
│ │ ├─ commit-msg.sample
│ │ ├─ fsmonitor-watchman.sample
│ │ ├─ post-update.sample
│ │ ├─ pre-applypatch.sample
│ │ ├─ pre-commit.sample
│ │ ├─ pre-merge-commit.sample
│ │ ├─ pre-push.sample
│ │ ├─ pre-rebase.sample
│ │ ├─ pre-receive.sample
│ │ ├─ prepare-commit-msg.sample
│ │ ├─ push-to-checkout.sample
│ │ └─ update.sample
│ ├─ info
│ │ └─ exclude
│ ├─ objects
│ │ ├─ info
│ │ └─ pack
│ └─ refs
│ ├─ heads
│ └─ tags
├─ .github
│ └─ dependabot.yml
├─ .gitignore
├─ Cargo.lock
├─ Cargo.toml
├─ README.md
├─ examples
│ └─ std
│ ├─ Cargo.lock
│ ├─ Cargo.toml
│ └─ src
│ └─ bin
│ └─ client.rs
└─ src
└─ lib.rs
I expect the program to compile as intended, as all trait bounds needed for WebsocketIO are implemented.