Reputation: 2174
Following https://stackoverflow.com/a/65924807/5884503 I'm adding associated types so I can return futures from my trait methods:
pub trait RtspClient: Runnable {
type PlayResult: Future<Output = std::result::Result<(), ()>>;
type ConnectResult: Future<Output = std::result::Result<(), RtspConnectionError>>;
type DisconnectResult: Future<Output = std::result::Result<(), RtspDisconnectionError>>;
fn connect(&self) -> RtspClient::ConnectResult {
Ok(())
}
fn disconnect(&self) -> RtspClient::DisconnectResult {
Ok(())
}
}
However, the more methods I add, the more associated types I get.
This wouldn't be a problem, except that I need to specify all of them when I need to use dyn RtspClient
:
|
12 | pub rtsp_client: Option<Arc<Mutex<dyn RtspClient>>>,
| ^^^^^^^^^^ help: specify the associated types: `RtspClient<PlayResult = Type, ConnectResult = Type, DisconnectResult = Type>`
|
Is there a way to force rust to use the default ones I already wrote in the trait definition?
Upvotes: 1
Views: 818
Reputation: 60132
There are multiple misunderstandings here. The Future<...>
part of the associated type is not a default it is a constraint. Future
is not a type, its a trait. Its saying an implementation's PlayResult
, for example, must satisfy the Future<...>
constraint.
A default associated type would look like this:
pub trait RtspClient {
type PlayResult = Ready<Result<(), ()>>; // using std::future::Ready as an example
}
But this syntax is not yet stabilized:
error[E0658]: associated type defaults are unstable
--> src/lib.rs:4:5
|
4 | type PlayResult = Ready<Result<(), ()>>; // using std::future::Ready as an example
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: see issue #29661 <https://github.com/rust-lang/rust/issues/29661> for more information
And even if you use a nightly compiler and enable the feature, you couldn't implement connect()
and disconnect()
by default because it must be valid for all possible implementations (you can do it in some cases, but I don't think this is one of them):
error[E0308]: mismatched types
--> src/lib.rs:13:9
|
12 | fn connect(&self) -> Self::ConnectResult {
| ------------------- expected `<Self as RtspClient>::ConnectResult` because of return type
13 | ready(Ok(()))
| ^^^^^^^^^^^^^ expected associated type, found struct `std::future::Ready`
|
= note: expected associated type `<Self as RtspClient>::ConnectResult`
found struct `std::future::Ready<Result<(), _>>`
error[E0308]: mismatched types
--> src/lib.rs:17:9
|
16 | fn disconnect(&self) -> Self::DisconnectResult {
| ---------------------- expected `<Self as RtspClient>::DisconnectResult` because of return type
17 | ready(Ok(()))
| ^^^^^^^^^^^^^ expected associated type, found struct `std::future::Ready`
|
= note: expected associated type `<Self as RtspClient>::DisconnectResult`
found struct `std::future::Ready<Result<(), _>>`
And even if that did work, you'll have problems using it as a trait object. Each implementation can have a different concrete associated types, but you need to specify the concrete associated types to use it as a trait object. They will undoubtedly not match:
struct MyDefaultClient;
impl RtspClient for MyDefaultClient {
// just uses the default types
fn connect(&self) -> Self::ConnectResult {
ready(Ok(()))
}
fn disconnect(&self) -> Self::DisconnectResult {
ready(Ok(()))
}
}
struct MyCustomClient;
impl RtspClient for MyCustomClient {
type ConnectResult = Pending<Result<(), RtspConnectionError>>; // different associated types
type DisconnectResult = Pending<Result<(), RtspDisconnectionError>>;
fn connect(&self) -> Self::ConnectResult {
pending()
}
fn disconnect(&self) -> Self::DisconnectResult {
pending()
}
}
fn main() {
let data = [
Box::new(MyDefaultClient) as Box<dyn RtspClient<ConnectResult = _, DisconnectResult = _>>,
Box::new(MyCustomClient),
];
}
The _
parts let the compiler infer the correct types, but will yield an error because dyn RtspClient<ConnectResult = A, ...>
is different than dyn RtspClient<ConnectResult = B>
.
error[E0271]: type mismatch resolving `<MyDefaultClient as RtspClient>::DisconnectResult == std::future::Pending<Result<(), RtspDisconnectionError>>`
--> src/main.rs:48:9
|
48 | Box::new(MyDefaultClient) as Box<dyn RtspClient<ConnectResult = _, DisconnectResult = _>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::future::Pending`, found struct `std::future::Ready`
|
= note: expected struct `std::future::Pending<Result<(), RtspDisconnectionError>>`
found struct `std::future::Ready<Result<(), RtspDisconnectionError>>`
= note: required for the cast to the object type `dyn RtspClient<DisconnectResult = std::future::Pending<Result<(), RtspDisconnectionError>>, ConnectResult = std::future::Pending<Result<(), RtspConnectionError>>>`
error[E0271]: type mismatch resolving `<MyDefaultClient as RtspClient>::ConnectResult == std::future::Pending<Result<(), RtspConnectionError>>`
--> src/main.rs:48:9
|
48 | Box::new(MyDefaultClient) as Box<dyn RtspClient<ConnectResult = _, DisconnectResult = _>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `std::future::Pending`, found struct `std::future::Ready`
|
= note: expected struct `std::future::Pending<Result<(), RtspConnectionError>>`
found struct `std::future::Ready<Result<(), RtspConnectionError>>`
= note: required for the cast to the object type `dyn RtspClient<DisconnectResult = std::future::Pending<Result<(), RtspDisconnectionError>>, ConnectResult = std::future::Pending<Result<(), RtspConnectionError>>>`
Associated types and trait objects do not mix in this fashion. You can think of them as regular generic types: Struct<A>
is a completely different type from Struct<B>
.
You should follow the other recommendations in the linked answer and use boxed futures, Pin<Box<Future<...>>>
, or the helper crate async-trait
instead of using associated types if you want to use different implementations interchangeably.
Upvotes: 4