关于rust:如何在结构中存储” impl Trait”类型的变量?

How do I store a variable of type `impl Trait` in a struct?

这有效:

1
2
3
4
5
6
7
8
9
10
11
let fut = Arc::new(Mutex::new(Box::pin(async { 1 })));

let mut conn_futures = BTreeMap::new(); // implicitly typed
conn_futures.insert(123, fut);
if let Some(fut) = conn_futures.get_mut(&123) {
   let fut = fut.clone();
   self.pool.spawn(async move {
        let mut fut = fut.try_lock().unwrap();
        (&mut *fut).await;
    });
};

如何在结构内编写相同的内容; conn_futures是什么类型?根据编译器,它是BTreeMap<i32, impl Future>,但是无法在结构中编写它:

1
2
3
struct Foo {
    conn_futures: BTreeMap<i32, impl Future>, // impl not allow in this position
}

我尝试过:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
use futures::{executor::LocalPool, lock::Mutex, task::SpawnExt, Future}; // 0.3.1
use std::{collections::BTreeMap, pin::Pin, sync::Arc};

struct Foo {
    conn_futures: BTreeMap<i32, Arc<Mutex<Pin<Box<dyn Future<Output = i32>>>>>>,
}

fn alternative() {
    let mut pool = LocalPool::new();
    let spawner = pool.spawner();

    // Have a structure with the btreemap instead
    let mut foo = Foo {
        conn_futures: BTreeMap::new(),
    };
    let fut = Arc::new(Mutex::new(Box::pin(async { 1 })));
    foo.conn_futures.insert(123, fut);
    if let Some(fut) = foo.conn_futures.get_mut(&123) {
        let fut = fut.clone();
        spawner.spawn(async move {
            let mut fut = fut.try_lock().unwrap();
            (&mut *fut).await;
        });
    };
}

fn main() {
    let mut pool = LocalPool::new();
    let spawner = pool.spawner();
    let fut = Arc::new(Mutex::new(Box::pin(async { 1 })));

    let mut conn_futures = BTreeMap::new(); // implicitly typed
    conn_futures.insert(123, fut);
    if let Some(fut) = conn_futures.get_mut(&123) {
        let fut = fut.clone();
        spawner.spawn(async move {
            let mut fut = fut.try_lock().unwrap();
            (&mut *fut).await;
        });
    };
}

游乐场

出现错误

1
2
3
4
5
6
7
8
error[E0308]: mismatched types
  --> src/main.rs:17:34
   |
17 |     foo.conn_futures.insert(123, fut);
   |                                  ^^^ expected trait core::future::future::Future, found opaque type
   |
   = note: expected type `std::sync::Arc<futures_util::lock::mutex::Mutex<std::pin::Pin<std::boxed::Box<(dyn core::future::future::Future<Output = i32> + 'static)>>>>`
              found type `std::sync::Arc<futures_util::lock::mutex::Mutex<std::pin::Pin<std::boxed::Box<impl core::future::future::Future>>>>`

如何在结构中声明conn_futures的类型?


您不能,真的。 impl Trait创建一个匿名的,无法命名的类型。这意味着您不能使用有效的显式类型声明变量。

主要解决方案是使用特征对象:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
use std::fmt::Display;

fn make_it() -> impl Display {
    2
}

struct Example {
    it: Box<dyn Display>,
}

impl Example {
    fn make() -> Self {
        Example {
            it: Box::new(make_it()),
        }
    }
}

您还可以避免使用关联的函数,而应使用普通函数,而不要使用泛型函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
use std::fmt::Display;

fn make_it() -> impl Display {
    2
}

struct Example< T > {
    it: T,
}

impl Example<Box<dyn Display>> {
    fn make() -> Self {
        Example {
            it: Box::new(make_it()),
        }
    }
}

fn make_example() -> Example<impl Display> {
    Example {
        it: make_it(),
    }
}

仅每晚

如果您希望使用不稳定的夜间功能,则可以使用存在性类型(RFC 2071):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 1.51.0-nightly (2021-01-03 80184183ba0a53aa4f49)
#![feature(type_alias_impl_trait)]

use std::fmt::Display;

type SomeDisplay = impl Display;

fn make_it() -> SomeDisplay {
    2
}

struct Example {
    it: SomeDisplay,
}

impl Example {
    fn make() -> Self {
        Example { it: make_it() }
    }
}

或:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 1.51.0-nightly (2021-01-03 80184183ba0a53aa4f49)
#![feature(type_alias_impl_trait)]

use std::fmt::Display;

fn make_it() -> impl Display {
    2
}

struct Example< T > {
    it: T,
}

type SomeDisplay = impl Display;

impl Example<SomeDisplay> {
    fn make() -> Self {
        Example { it: make_it() }
    }
}

另请参见:

  • 返回Iterator(或任何其他特征)的正确方法是什么?
  • 为什么不能使用impl trait返回多个/条件类型?
  • 是否可以在特征定义中将" impl Trait"用作函数的返回类型?
  • 是什么使某物成为"特质对象"?
  • 在板条箱的API中发布具体类型而不是" impl trait"的好处是什么?

尽管以上建议很有用,但该问题的具体答案是适当地投射Pin<Box<Future>>>

此行

1
let fut = Arc::new(Mutex::new(Box::pin(async { 1 })));

需要更改

1
let fut = Arc::new(Mutex::new(Box::pin(async { 1 }) as Pin<Box<Future<Output=i32>>> ));

这将允许人们表达以下struct

1
2
3
struct Foo {
    conn_futures: BTreeMap<ChannelId, Arc<Mutex<Pin<Box<dyn Future<Output = i32>>>>>>,
}

,编译器不会抱怨。感谢@Aloso提供的提示

但是,将给出以下错误

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
error[E0277]: `(dyn core::future::future::Future<Output = i32> + 'static)` cannot be sent between threads safely
  --> src/main.rs:24:16
   |
24 |        spawner.spawn(async move {
   |                ^^^^^ `(dyn core::future::future::Future<Output = i32> + 'static)` cannot be sent between threads safely
   |
   = help: the trait `std::marker::Send` is not implemented for `(dyn core::future::future::Future<Output = i32> + 'static)`
   = note: required because of the requirements on the impl of `std::marker::Send` for `std::ptr::Unique<(dyn core::future::future::Future<Output = i32> + 'static)>`
   = note: required because it appears within the type `std::boxed::Box<(dyn core::future::future::Future<Output = i32> + 'static)>`
   = note: required because it appears within the type `std::pin::Pin<std::boxed::Box<(dyn core::future::future::Future<Output = i32> + 'static)>>`
   = note: required because of the requirements on the impl of `std::marker::Send` for `futures_util::lock::mutex::Mutex<std::pin::Pin<std::boxed::Box<(dyn core::future::future::Future<Output = i32> + 'static)>>>`
   = note: required because of the requirements on the impl of `std::marker::Send` for `std::sync::Arc<futures_util::lock::mutex::Mutex<std::pin::Pin<std::boxed::Box<(dyn core::future::future::Future<Output = i32> + 'static)>>>>`
   = note: required because it appears within the type `[static generator@src/main.rs:24:33: 27:10 fut:std::sync::Arc<futures_util::lock::mutex::Mutex<std::pin::Pin<std::boxed::Box<(dyn core::future::future::Future<Output = i32> + 'static)>>>> _]`
   = note: required because it appears within the type `std::future::GenFuture<[static generator@src/main.rs:24:33: 27:10 fut:std::sync::Arc<futures_util::lock::mutex::Mutex<std::pin::Pin<std::boxed::Box<(dyn core::future::future::Future<Output = i32> + 'static)>>>> _]>`
   = note: required because it appears within the type `impl core::future::future::Future`

这将是一个单独的问题