我可以将 `impl Future` 存储为具体类型吗?
Can I store an `impl Future` as a concrete type?
tokio::net::TcpStream::connect
是一个异步函数,这意味着它 return 是一个存在类型,impl Future
。我想将这些期货的 Vec
存储在一个结构中。我发现很多问题,有人想在列表中存储多个 different impl Future
,但我只想存储 return 类型的一个。我觉得如果没有 Box<dyn Future>
这应该是可能的,因为我实际上只存储了一个具体类型,但是如果没有 found opaque type
错误我无法弄清楚如何做到这一点。
夜间功能可以实现 min_type_alias_impl_trait
。诀窍是创建一个类型别名和一个虚拟函数,编译器可以从中推断出定义用途。
#![feature(min_type_alias_impl_trait)]
use tokio::net::TcpStream;
use core::future::Future;
type TcpStreamConnectFut = impl Future<Output = std::io::Result<TcpStream>>;
fn __tcp_stream_connect_defining_use() -> TcpStreamConnectFut {
TcpStream::connect("127.0.0.1:8080")
}
struct Foo {
connection_futs: Vec<TcpStreamConnectFut>,
}
这可以编译,但没有按预期工作:
impl Foo {
fn push(&mut self) {
self.connection_futs.push(TcpStream::connect("127.0.0.1:8080"));
}
}
error[E0308]: mismatched types
--> src/lib.rs:18:35
|
6 | type TcpStreamConnectFut = impl Future<Output = std::io::Result<TcpStream>>;
| ------------------------------------------------ the expected opaque type
...
18 | self.connection_futs.push(TcpStream::connect("127.0.0.1:8080"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected opaque type, found a different opaque type
|
= note: while checking the return type of the `async fn`
= note: expected opaque type `impl Future` (opaque type at <src/lib.rs:6:28>)
found opaque type `impl Future` (opaque type at </playground/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.7.1/src/net/tcp/stream.rs:111:56>)
= help: consider `await`ing on both `Future`s
= note: distinct uses of `impl Trait` result in different opaque types
使用我们创建的虚拟函数可以工作:
impl Foo {
fn push(&mut self) {
self.connection_futs.push(__tcp_stream_connect_defining_use());
}
}
所以我们可以创建包装函数:
fn tcp_stream_connect<A: ToSocketAddrs>(addr: A) -> TcpStreamConnectFut {
TcpStream::connect(addr)
}
除了...
error: type parameter `A` is part of concrete type but not used in parameter list for the `impl Trait` type alias
--> src/main.rs:9:74
|
9 | fn tcp_stream_connect<A: ToSocketAddrs>(addr: A) -> TcpStreamConnectFut {
| __________________________________________________________________________^
10 | | TcpStream::connect(addr)
11 | | }
| |_^
我们可以只使用 String
或 &'static str
,然后整个编译:
type TcpStreamConnectFut = impl Future<Output = std::io::Result<TcpStream>>;
fn tcp_stream_connect(addr: &'static str) -> TcpStreamConnectFut {
TcpStream::connect(addr)
}
struct Foo {
connection_futs: Vec<TcpStreamConnectFut>,
}
impl Foo {
fn push(&mut self) {
self.connection_futs.push(tcp_stream_connect("..."));
}
}
您还可以将通用参数添加到类型别名本身,但在这种情况下这可能没有意义:
type TcpStreamConnectFut<A> = impl Future<Output = std::io::Result<TcpStream>>;
fn tcp_stream_connect<A: ToSocketAddrs>(addr: A) -> TcpStreamConnectFut<A> {
TcpStream::connect(addr)
}
struct Foo {
connection_futs: Vec<TcpStreamConnectFut<&'static str>>,
}
impl Foo {
fn push(&mut self) {
self.connection_futs.push(tcp_stream_connect("..."));
}
}
所以 是 可能的,但是有一些限制。我不确定其中有多少是错误,有多少是故意行为。已经讨论过 typeof
运算符可以使这更容易,但这就是我们目前所拥有的。
你可以使用旧的 Vec
,就像这样:
use core::future::Future;
use tokio::net::TcpStream;
fn just_vec() -> Vec<impl Future<Output = std::io::Result<TcpStream>>> {
let mut v = Vec::new();
// connect to several streams
v.push(TcpStream::connect("127.0.0.1:8080"));
v.push(TcpStream::connect("127.0.0.2:8080"));
v
}
但是,如果您想将它存储在一个结构中,它会变得更加棘手,因为与上面不同的是,在可以推断具体类型的情况下,您需要更明确地使用结构。
一种稳定的方法是使用通用结构。这实际上与存储闭包非常相似(您也没有具体类型)。
use core::future::Future;
use tokio::net::TcpStream;
use tokio::io::AsyncWriteExt;
struct Foo<T> {
connections: Vec<T>,
}
/// This is just like any other vec-wrapper
impl<T> Foo<T> {
pub fn new() -> Self {
Self {
connections: Vec::new(),
}
}
pub fn push(&mut self, conn: T) {
self.connections.push(conn);
}
}
/// Some more specific functios that actually need the Future
impl<T> Foo<T> where T: Future<Output = std::io::Result<TcpStream>> {
pub async fn broadcast(self, data: &[u8]) -> std::io::Result<()> {
for stream in self.connections {
stream.await?.write_all(data).await?
}
Ok(())
}
}
async fn with_struct() -> std::io::Result<()> {
let mut foo = Foo::new();
// connect to several streams
foo.push(TcpStream::connect("127.0.0.1:8080"));
foo.push(TcpStream::connect("127.0.0.2:8080"));
// Do something with the connections
foo.broadcast(&[1,2,3]).await
}
tokio::net::TcpStream::connect
是一个异步函数,这意味着它 return 是一个存在类型,impl Future
。我想将这些期货的 Vec
存储在一个结构中。我发现很多问题,有人想在列表中存储多个 different impl Future
,但我只想存储 return 类型的一个。我觉得如果没有 Box<dyn Future>
这应该是可能的,因为我实际上只存储了一个具体类型,但是如果没有 found opaque type
错误我无法弄清楚如何做到这一点。
夜间功能可以实现 min_type_alias_impl_trait
。诀窍是创建一个类型别名和一个虚拟函数,编译器可以从中推断出定义用途。
#![feature(min_type_alias_impl_trait)]
use tokio::net::TcpStream;
use core::future::Future;
type TcpStreamConnectFut = impl Future<Output = std::io::Result<TcpStream>>;
fn __tcp_stream_connect_defining_use() -> TcpStreamConnectFut {
TcpStream::connect("127.0.0.1:8080")
}
struct Foo {
connection_futs: Vec<TcpStreamConnectFut>,
}
这可以编译,但没有按预期工作:
impl Foo {
fn push(&mut self) {
self.connection_futs.push(TcpStream::connect("127.0.0.1:8080"));
}
}
error[E0308]: mismatched types
--> src/lib.rs:18:35
|
6 | type TcpStreamConnectFut = impl Future<Output = std::io::Result<TcpStream>>;
| ------------------------------------------------ the expected opaque type
...
18 | self.connection_futs.push(TcpStream::connect("127.0.0.1:8080"));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected opaque type, found a different opaque type
|
= note: while checking the return type of the `async fn`
= note: expected opaque type `impl Future` (opaque type at <src/lib.rs:6:28>)
found opaque type `impl Future` (opaque type at </playground/.cargo/registry/src/github.com-1ecc6299db9ec823/tokio-1.7.1/src/net/tcp/stream.rs:111:56>)
= help: consider `await`ing on both `Future`s
= note: distinct uses of `impl Trait` result in different opaque types
使用我们创建的虚拟函数可以工作:
impl Foo {
fn push(&mut self) {
self.connection_futs.push(__tcp_stream_connect_defining_use());
}
}
所以我们可以创建包装函数:
fn tcp_stream_connect<A: ToSocketAddrs>(addr: A) -> TcpStreamConnectFut {
TcpStream::connect(addr)
}
除了...
error: type parameter `A` is part of concrete type but not used in parameter list for the `impl Trait` type alias
--> src/main.rs:9:74
|
9 | fn tcp_stream_connect<A: ToSocketAddrs>(addr: A) -> TcpStreamConnectFut {
| __________________________________________________________________________^
10 | | TcpStream::connect(addr)
11 | | }
| |_^
我们可以只使用 String
或 &'static str
,然后整个编译:
type TcpStreamConnectFut = impl Future<Output = std::io::Result<TcpStream>>;
fn tcp_stream_connect(addr: &'static str) -> TcpStreamConnectFut {
TcpStream::connect(addr)
}
struct Foo {
connection_futs: Vec<TcpStreamConnectFut>,
}
impl Foo {
fn push(&mut self) {
self.connection_futs.push(tcp_stream_connect("..."));
}
}
您还可以将通用参数添加到类型别名本身,但在这种情况下这可能没有意义:
type TcpStreamConnectFut<A> = impl Future<Output = std::io::Result<TcpStream>>;
fn tcp_stream_connect<A: ToSocketAddrs>(addr: A) -> TcpStreamConnectFut<A> {
TcpStream::connect(addr)
}
struct Foo {
connection_futs: Vec<TcpStreamConnectFut<&'static str>>,
}
impl Foo {
fn push(&mut self) {
self.connection_futs.push(tcp_stream_connect("..."));
}
}
所以 是 可能的,但是有一些限制。我不确定其中有多少是错误,有多少是故意行为。已经讨论过 typeof
运算符可以使这更容易,但这就是我们目前所拥有的。
你可以使用旧的 Vec
,就像这样:
use core::future::Future;
use tokio::net::TcpStream;
fn just_vec() -> Vec<impl Future<Output = std::io::Result<TcpStream>>> {
let mut v = Vec::new();
// connect to several streams
v.push(TcpStream::connect("127.0.0.1:8080"));
v.push(TcpStream::connect("127.0.0.2:8080"));
v
}
但是,如果您想将它存储在一个结构中,它会变得更加棘手,因为与上面不同的是,在可以推断具体类型的情况下,您需要更明确地使用结构。
一种稳定的方法是使用通用结构。这实际上与存储闭包非常相似(您也没有具体类型)。
use core::future::Future;
use tokio::net::TcpStream;
use tokio::io::AsyncWriteExt;
struct Foo<T> {
connections: Vec<T>,
}
/// This is just like any other vec-wrapper
impl<T> Foo<T> {
pub fn new() -> Self {
Self {
connections: Vec::new(),
}
}
pub fn push(&mut self, conn: T) {
self.connections.push(conn);
}
}
/// Some more specific functios that actually need the Future
impl<T> Foo<T> where T: Future<Output = std::io::Result<TcpStream>> {
pub async fn broadcast(self, data: &[u8]) -> std::io::Result<()> {
for stream in self.connections {
stream.await?.write_all(data).await?
}
Ok(())
}
}
async fn with_struct() -> std::io::Result<()> {
let mut foo = Foo::new();
// connect to several streams
foo.push(TcpStream::connect("127.0.0.1:8080"));
foo.push(TcpStream::connect("127.0.0.2:8080"));
// Do something with the connections
foo.broadcast(&[1,2,3]).await
}