The split
method is defined on the StreamExt
trait. Since WebSocketStream<S>
implements StreamExt
, you can call StreamExt
methods on WebSocketStream<S>
. But only when StreamExt
is imported.
You can't find split
in the websocket source code because it has a default implementation where StreamExt
is defined. You can find it here: link
The WebSocketStream<S>
type implements StreamExt
because of this line of code:
impl<T> Stream for WebSocketStream<T>
where
T: AsyncRead + AsyncWrite + Unpin,
{ ... }
link
and this:
impl<T: ?Sized> StreamExt for T where T: Stream {}
link
The first snippet says "WebSocketStream<T>
implements Stream
whenever T
implements the AsyncRead
, AsyncWrite
, and Unpin
traits.
The second snippet says "anything that implements Stream
also implements StreamExt
". This is called a blanket implementation.
Thus, since WebSocketStream<T>
implements Stream
, it must also implement StreamExt
.
To get back to the original question: To call a trait method, the trait must be imported. This is always the case for all traits and all trait methods. Under all circumstances. Including this one.
Why?
Because traits can be implemented for types outside of your crate. For example:
trait StringExt {
fn twice_len(&self) -> usize;
}
impl StringExt for String {
fn twice_len(&self) -> usize {
self.len() * 2
}
}
impl StringExt for &str {
fn twice_len(&self) -> usize {
self.len() * 2
}
}
fn main() {
println!("{}", "foo".twice_len());
}
This adds a new method to String
. Imagine if you import a crate containing the above. Should this magically add a new method to string types in the rest of the project?
No, it does not. To use twice_len
, you must import StringExt
. That avoids weird situations where adding a new dependency could add new methods without otherwise changing the source code.