Rust compiler does not unify types that both `impl Future<Output=()>` in branches of `if let` expression

huangapple go评论71阅读模式
英文:

Rust compiler does not unify types that both `impl Future<Output=()>` in branches of `if let` expression

问题

I'm here to provide the translation of the code and relevant parts. Here's the translated code segment:

在尝试有条件地创建一个未来对象,以便稍后在 `select_biased!` 中使用时,我遇到了编译错误:

```rust
            let mut heartbeat_timer = if let ServerState::Leader(_, _, _) = server_state {
                // 如果我们是 Leader,我们希望以维持领导地位所需的最小间隔发送 AppendEntries RPC
                task::sleep(HEARTBEAT_DURATION)
            } else {
                // 如果我们不是领导者,就不需要发送这些
                pending::<()>()
            };

// ...

            select_biased! {
                h = heartbeat_timer => self.heartbeat().await,
                // ... 一些其他按优先级声明的选项
                default => task::yield_now().await,
            }

我很难理解为什么会出现编译错误:

error[E0308]: `if`  `else` 具有不兼容的类型
   --> src/raft.rs:185:17
    |
182 |               let heartbeat_timer = if let ServerState::Leader(_, _, _) = server_state {
    |  ___________________________________-
183 | |                 task::sleep(HEARTBEAT_DURATION)
    | |                 ------------------------------- 期望的类型是因为这个
184 | |             } else {
185 | |                 pending::<()>()
    | |                 ^^^^^^^^^^^^^^^ 期望的是 Future,但找到了 `Pending<()>`
186 | |             };
    | |_____________ `if`  `else` 具有不兼容的类型
    |
    = note: 期望的是不透明类型 `impl futures::Future<Output = ()>`
                    找到的是结构体 `futures::future::Pending<()>`

通过在我的IDE中点击 pending,我可以看到它的类型是 Pending<()>,而且我还可以进一步看到 Pending<()> 实现了 Future<Output=T>

impl<T> Future for Pending<T> {
    type Output = T;

    fn poll(self: Pin<&mut Self>, _: &mut Context<'_>) -> Poll<T> {
        Poll::Pending
    }
}

出于某种原因,我还不能理解,编译器未能遵循相同的路径。编译器知道的秘密是什么,我漏掉了什么?是否有我的代码的直接转换可以满足编译器?我应该考虑什么,可以使我提供编译器需要的内容?

rustup --version 包含以下输出:

info: 当前活跃的 `rustc` 版本是 `rustc 1.69.0 (84c898d65 2023-04-16)`

请注意,我已经翻译了您的代码和相关的部分,但不提供其他回答或问题的翻译。

英文:

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&#39;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&#39;re not the leader, no need to send those
                pending::&lt;()&gt;()
            };

// ...

            select_biased! {
                h = heartbeat_timer =&gt; self.heartbeat().await,
                // ... some others declared in priority order
                default =&gt; task::yield_now().await,
            }

I am having difficulty understanding why I get a compilation error:

error[E0308]: `if` and `else` have incompatible types
   --&gt; 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::&lt;()&gt;()
    | |                 ^^^^^^^^^^^^^^^ expected future, found `Pending&lt;()&gt;`
186 | |             };
    | |_____________- `if` and `else` have incompatible types
    |
    = note: expected opaque type `impl futures::Future&lt;Output = ()&gt;`
                    found struct `futures::future::Pending&lt;()&gt;`

Clicking (in my IDE) through pending shows me that its type is Pending&lt;T&gt;, and I can further see that Pending&lt;T&gt; implements Future&lt;Output=T&gt;:

impl&lt;T&gt; Future for Pending&lt;T&gt; {
    type Output = T;

    fn poll(self: Pin&lt;&amp;mut Self&gt;, _: &amp;mut Context&lt;&#39;_&gt;) -&gt; Poll&lt;T&gt; {
        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)`

答案1

得分: 3

impl 返回类型不以这种方式工作,不幸的是。它们不是动态分发;一个 impl 返回类型仍然必须是一个单一具体类型;只是你不必在代码中指定类型。如果你有两个(或更多)分支分支返回不同的类型,那么 impl 返回将会拒绝它。如果你想要这样做,你必须使用完全的动态分发。这意味着一个 dyn 特性对象和一个 Box 或其他间接机制。

在你的情况下,我建议将 impl Future&lt;Output=()&gt; 替换为 Box&lt;dyn Future&lt;Output=()&gt;&gt;。在每个分支上,你将不得不对所需的返回值进行显式的 Box::new 包装,以添加间接层。

英文:

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&lt;Output=()&gt; with Box&lt;dyn Future&lt;Output=()&gt;&gt;. 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.

huangapple
  • 本文由 发表于 2023年5月30日 10:46:30
  • 转载请务必保留本文链接:https://go.coder-hub.com/76361317.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定