Reputation: 609
While trying to create a Future conditionally, for later use with select_biased!
:
let mut heartbeat_timer = if let ServerState::Leader(_, _, _) = server_state {
// if we're the Leader, we want to send out AppendEntries RPC at a
// minimum interval as required to maintain our leadership
task::sleep(HEARTBEAT_DURATION)
} else {
// if we're not the leader, no need to send those
pending::<()>()
};
// ...
select_biased! {
h = heartbeat_timer => self.heartbeat().await,
// ... some others declared in priority order
default => task::yield_now().await,
}
I am having difficulty understanding why I get a compilation error:
error[E0308]: `if` and `else` have incompatible types
--> src/raft.rs:185:17
|
182 | let heartbeat_timer = if let ServerState::Leader(_, _, _) = server_state {
| ___________________________________-
183 | | task::sleep(HEARTBEAT_DURATION)
| | ------------------------------- expected because of this
184 | | } else {
185 | | pending::<()>()
| | ^^^^^^^^^^^^^^^ expected future, found `Pending<()>`
186 | | };
| |_____________- `if` and `else` have incompatible types
|
= note: expected opaque type `impl futures::Future<Output = ()>`
found struct `futures::future::Pending<()>`
Clicking (in my IDE) through pending
shows me that its type is Pending<T>
, and I can further see that Pending<T>
implements Future<Output=T>
:
impl<T> Future for Pending<T> {
type Output = T;
fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<T> {
Poll::Pending
}
}
For some reason I can't yet understand, the compiler fails to follow the same breadcrumbs. What secret does the compiler know that am I missing? Is there some straightforward transformation of my code that would satisfy the compiler? What should I be thinking that would have led me to supply what the compiler needs?
rustup --version
includes the output:
info: The currently active `rustc` version is `rustc 1.69.0 (84c898d65 2023-04-16)`
Upvotes: 1
Views: 83
Reputation: 70257
impl
return types don't work that way, unfortunately. They aren't dynamic dispatch; an impl
return type must still be a single concrete type; it's just that you don't have to specify the type in your code. If you have two (or more) branch arms that return different types, then the impl
return will reject it. If you want to do that, you have to use full dynamic dispatch. That means a dyn
trait object and a Box
or other indirection mechanism.
In your case, I might recommend replacing impl Future<Output=()>
with Box<dyn Future<Output=()>>
. On each of the branches, you'll have to do an explicit Box::new
around the desired return value to add the layer of indirection.
Upvotes: 3