常见问题汇总

来源:https://rust-zh.github.io/faq/

欢迎贡献:

更多阅读: https://rust-zh.github.io/faq/


本期摘录:

如何同时等待多个 Future?

如果想要等待多个 Future 都完成后返回,对于固定数量的 Future 可以使用 futures 所提供的 joinjoin3join4 等函数,或者 tokio 所提供的 join! 宏,将多个 Future 合并为一个进行等待。对于不定数量的 Future,比如有一个 Vec,则可以使用 futuresjoin_all 函数。

若要在数个 Future 中第一个错误发生时就返回,则可以使用它们对应的 try_jointry_join3try_join4try_join_all 等函数以及 try_join! 宏。

如果想要在多个 Future 中的第一个完成后就返回,可以使用 futures 的 selectselect_allselect_ok 函数或 tokio 的 select! 宏。

需要注意的是,上面这些函数和宏在每次被轮询的时候,都会轮询里面每一个未完成的 Future,因此当其所带的 Future 很多时可能会有性能问题。

使用 futures 提供的 FuturesOrderedFuturesUnordered 这两个结构可以解决这一问题。它们是为管理大量的 Future 而设计的,只会去轮询收到了唤醒通知的 Future。它们将这些 Future 聚合成一个 Stream 逐个返回里面 Future 的结果,其中前者会按照输入的 Future 的顺序返回,而后者则是以任意顺序(可以近似看作按照完成顺序)返回。

# 新创建的空 Vec<T> 的指针为何指向1248等地址?

Vec 的容量为0时,没有合法的操作会向其指针指向的位置进行读取和写入,进行任何读写之前都必然会有一次内存分配,因此这个初始的指针并不需要是一个有效的指针。这也使得创建 Vec 本身没有进行实际内存分配的必要,既省去了内存分配的开销,也让创建容器的操作可以在常量上下文中使用。

而因为 Vec 需要能被作为[切片]使用,由于切片对数据指针的要求,它的指针的地址需要是非空并且正确对齐的,因而简单起见便选择了类型的对齐的大小作为这个无效指针指向的地址。

如果有一个 trait Foo: Base,如何将一个 &dyn Foo 转换到 &dyn Base

Rust 目前不直接提供这种转换,如果需要转换可以使用一个中间[特质]来实现,如


#![allow(unused)]
fn main() {
trait Base {
    // ...
}

trait AsBase {
    fn as_base(&self) -> &dyn Base;
}

impl<T: Base> AsBase for T {
    fn as_base(&self) -> &dyn Base { self }
}

trait Foo: AsBase {
    // ...
}
}

不支持的主要原因是在[特质对象]的[虚表]中没有相应的数据指向另一个特质的虚表,而不提供相应数据的原因可能是由于这很容易产生过多无用的虚表,进而导致二进制体积的膨胀。

更多关于这一话题的讨论可以参考 RFC 2765 以及 Traits, dynamic dispatch and upcasting